OSDN Git Service

adjust for patch
[jnethack/source.git] / win / win32 / mhmsgwnd.c
1 /* NetHack 3.6  mhmsgwnd.c      $NHDT-Date: 1432512812 2015/05/25 00:13:32 $  $NHDT-Branch: master $:$NHDT-Revision: 1.32 $ */
2 /* Copyright (C) 2001 by Alex Kompel     */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "winMS.h"
6 #include "mhmsgwnd.h"
7 #include "mhmsg.h"
8 #include "mhfont.h"
9
10 #define MSG_WRAP_TEXT
11
12 #define MSG_VISIBLE_LINES max(iflags.wc_vary_msgcount, 2)
13 #define MAX_MSG_LINES 128
14 #define MSG_LINES (int) min(iflags.msg_history, MAX_MSG_LINES)
15 #define MAXWINDOWTEXT TBUFSZ
16
17 #define DEFAULT_COLOR_BG_MSG COLOR_WINDOW
18 #define DEFAULT_COLOR_FG_MSG COLOR_WINDOWTEXT
19
20 #define MORE "--More--"
21
22 struct window_line {
23     int attr;
24     char text[MAXWINDOWTEXT + 1];
25 };
26
27 typedef struct mswin_nethack_message_window {
28     size_t max_text;
29     struct window_line window_text[MAX_MSG_LINES];
30     int lines_last_turn; /* lines added during the last turn */
31     int lines_not_seen;  /* lines not yet seen by user after last turn or
32                             --More-- */
33     int nevermore;       /* We want no more --More-- prompts */
34
35     int xChar;  /* horizontal scrolling unit */
36     int yChar;  /* vertical scrolling unit */
37     int xUpper; /* average width of uppercase letters */
38     int xPos;   /* current horizontal scrolling position */
39     int yPos;   /* current vertical scrolling position */
40     int xMax;   /* maximum horizontal scrolling position */
41     int yMax;   /* maximum vertical scrolling position */
42     int xPage;  /* page size of horizontal scroll bar */
43 } NHMessageWindow, *PNHMessageWindow;
44
45 static TCHAR szMessageWindowClass[] = TEXT("MSNHMessageWndClass");
46 LRESULT CALLBACK NHMessageWndProc(HWND, UINT, WPARAM, LPARAM);
47 static void register_message_window_class(void);
48 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
49 static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
50 #ifndef MSG_WRAP_TEXT
51 static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
52 #endif
53 static COLORREF setMsgTextColor(HDC hdc, int gray);
54 static void onPaint(HWND hWnd);
55 static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
56 static BOOL can_append_text(HWND hWnd, int attr, const char *text);
57 /* check if text can be appended to the last line without wrapping */
58
59 static BOOL more_prompt_check(HWND hWnd);
60 /* check if "--more--" prompt needs to be displayed */
61
62 #ifdef USER_SOUNDS
63 extern void play_sound_for_message(const char *str);
64 #endif
65
66 HWND
67 mswin_init_message_window()
68 {
69     static int run_once = 0;
70     HWND ret;
71     DWORD style;
72     RECT rt;
73
74     if (!run_once) {
75         register_message_window_class();
76         run_once = 1;
77     }
78
79     /* get window position */
80     if (GetNHApp()->bAutoLayout) {
81         SetRect(&rt, 0, 0, 0, 0);
82     } else {
83         mswin_get_window_placement(NHW_MESSAGE, &rt);
84     }
85
86 #ifdef MSG_WRAP_TEXT
87     style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_SIZEBOX;
88 #else
89     style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL | WS_SIZEBOX;
90 #endif
91
92     ret = CreateWindowEx(
93         WS_EX_CLIENTEDGE, szMessageWindowClass, /* registered class name */
94         NULL,                                   /* window name */
95         style,                                  /* window style */
96         rt.left,              /* horizontal position of window */
97         rt.top,               /* vertical position of window */
98         rt.right - rt.left,   /* window width */
99         rt.bottom - rt.top,   /* window height */
100         GetNHApp()->hMainWnd, /* handle to parent or owner window */
101         NULL,                 /* menu handle or child identifier */
102         GetNHApp()->hApp,     /* handle to application instance */
103         NULL);                /* window-creation data */
104
105     if (!ret)
106         panic("Cannot create message window");
107
108     /* Set window caption */
109     SetWindowText(ret, "Messages");
110
111     return ret;
112 }
113
114 void
115 register_message_window_class()
116 {
117     WNDCLASS wcex;
118     ZeroMemory(&wcex, sizeof(wcex));
119
120     wcex.style = CS_NOCLOSE;
121     wcex.lpfnWndProc = (WNDPROC) NHMessageWndProc;
122     wcex.cbClsExtra = 0;
123     wcex.cbWndExtra = 0;
124     wcex.hInstance = GetNHApp()->hApp;
125     wcex.hIcon = NULL;
126     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
127     wcex.hbrBackground = message_bg_brush
128                              ? message_bg_brush
129                              : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MSG);
130     wcex.lpszMenuName = NULL;
131     wcex.lpszClassName = szMessageWindowClass;
132
133     RegisterClass(&wcex);
134 }
135
136 LRESULT CALLBACK
137 NHMessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
138 {
139     switch (message) {
140     case WM_CREATE:
141         onCreate(hWnd, wParam, lParam);
142         break;
143
144     case WM_MSNH_COMMAND:
145         onMSNHCommand(hWnd, wParam, lParam);
146         break;
147
148     case WM_PAINT:
149         onPaint(hWnd);
150         break;
151
152     case WM_SETFOCUS:
153         SetFocus(GetNHApp()->hMainWnd);
154         break;
155
156 #ifndef MSG_WRAP_TEXT
157     case WM_HSCROLL:
158         onMSNH_HScroll(hWnd, wParam, lParam);
159         break;
160 #endif
161
162     case WM_VSCROLL:
163         onMSNH_VScroll(hWnd, wParam, lParam);
164         break;
165
166     case WM_DESTROY: {
167         PNHMessageWindow data;
168         data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
169         free(data);
170         SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
171     } break;
172
173     case WM_SIZE: {
174         SCROLLINFO si;
175         int xNewSize;
176         int yNewSize;
177         PNHMessageWindow data;
178         RECT rt;
179
180         data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
181
182         xNewSize = LOWORD(lParam);
183         yNewSize = HIWORD(lParam);
184
185         if (xNewSize > 0 || yNewSize > 0) {
186 #ifndef MSG_WRAP_TEXT
187             data->xPage = xNewSize / data->xChar;
188             data->xMax = max(0, (int) (1 + data->max_text - data->xPage));
189             data->xPos = min(data->xPos, data->xMax);
190
191             ZeroMemory(&si, sizeof(si));
192             si.cbSize = sizeof(si);
193             si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
194             si.nMin = 0;
195             si.nMax = data->max_text;
196             si.nPage = data->xPage;
197             si.nPos = data->xPos;
198             SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
199 #endif
200
201             data->yMax = MSG_LINES - 1;
202             data->yPos = min(data->yPos, data->yMax);
203
204             ZeroMemory(&si, sizeof(si));
205             si.cbSize = sizeof(si);
206             si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
207             si.nMin = MSG_VISIBLE_LINES;
208             si.nMax = data->yMax + MSG_VISIBLE_LINES - 1;
209             si.nPage = MSG_VISIBLE_LINES;
210             si.nPos = data->yPos;
211             SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
212         }
213
214         /* update NetHack internal window position */
215         GetWindowRect(hWnd, &rt);
216         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
217         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
218         mswin_update_window_placement(NHW_MESSAGE, &rt);
219
220         /* redraw window - it does not handle incremental resizing too well */
221         InvalidateRect(hWnd, NULL, TRUE);
222     } break;
223
224     case WM_MOVE: {
225         RECT rt;
226         GetWindowRect(hWnd, &rt);
227         ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
228         ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
229         mswin_update_window_placement(NHW_MESSAGE, &rt);
230     } break;
231
232     default:
233         return DefWindowProc(hWnd, message, wParam, lParam);
234     }
235     return 0;
236 }
237
238 void
239 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
240 {
241     PNHMessageWindow data;
242
243     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
244     switch (wParam) {
245     case MSNH_MSG_PUTSTR: {
246         PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
247         SCROLLINFO si;
248 #if 0 /*JP*/
249         char *p;
250 #else
251         unsigned char *p;
252 #endif
253
254         if (msg_data->append == 1) {
255             /* Forcibly append to line, even if we pass the edge */
256             strncat(data->window_text[MSG_LINES - 1].text, msg_data->text,
257                     MAXWINDOWTEXT
258                         - strlen(data->window_text[MSG_LINES - 1].text));
259         } else if (msg_data->append < 0) {
260             /* remove that many chars */
261             int len = strlen(data->window_text[MSG_LINES - 1].text);
262             int newend = max(len + msg_data->append, 0);
263             data->window_text[MSG_LINES - 1].text[newend] = '\0';
264         } else {
265             if (can_append_text(hWnd, msg_data->attr, msg_data->text)) {
266                 strncat(data->window_text[MSG_LINES - 1].text, "  ",
267                         MAXWINDOWTEXT
268                             - strlen(data->window_text[MSG_LINES - 1].text));
269                 strncat(data->window_text[MSG_LINES - 1].text, msg_data->text,
270                         MAXWINDOWTEXT
271                             - strlen(data->window_text[MSG_LINES - 1].text));
272             } else {
273                 /* check for "--more--" */
274                 if (!data->nevermore && more_prompt_check(hWnd)) {
275                     int okkey = 0;
276                     int chop;
277                     // @@@ Ok respnses
278
279                     /* append more prompt and inticate the update */
280                     strncat(
281                         data->window_text[MSG_LINES - 1].text, MORE,
282                         MAXWINDOWTEXT
283                             - strlen(data->window_text[MSG_LINES - 1].text));
284                     InvalidateRect(hWnd, NULL, TRUE);
285
286                     /* get the input */
287                     while (!okkey) {
288                         int c = mswin_nhgetch();
289
290                         switch (c) {
291                         /* space or enter */
292                         case ' ':
293                         case '\015':
294                             okkey = 1;
295                             break;
296                         /* ESC */
297                         case '\033':
298                             data->nevermore = 1;
299                             okkey = 1;
300                             break;
301                         default:
302                             break;
303                         }
304                     }
305
306                     /* erase the "--more--" prompt */
307                     chop = strlen(data->window_text[MSG_LINES - 1].text)
308                            - strlen(MORE);
309                     data->window_text[MSG_LINES - 1].text[chop] = '\0';
310                     data->lines_not_seen = 0;
311                 }
312
313                 /* check if the string is empty */
314                 for (p = data->window_text[MSG_LINES - 1].text;
315                      *p && isspace(*p); p++)
316                     ;
317
318                 if (*p) {
319                     /* last string is not empty - scroll up */
320                     memmove(&data->window_text[0], &data->window_text[1],
321                             (MSG_LINES - 1) * sizeof(data->window_text[0]));
322                 }
323
324                 /* append new text to the end of the array */
325                 data->window_text[MSG_LINES - 1].attr = msg_data->attr;
326                 strncpy(data->window_text[MSG_LINES - 1].text, msg_data->text,
327                         MAXWINDOWTEXT);
328
329                 data->lines_not_seen++;
330                 data->lines_last_turn++;
331             }
332         }
333
334         /* reset V-scroll position to display new text */
335         data->yPos = data->yMax;
336
337         ZeroMemory(&si, sizeof(si));
338         si.cbSize = sizeof(si);
339         si.fMask = SIF_POS;
340         si.nPos = data->yPos;
341         SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
342
343         /* update window content */
344         InvalidateRect(hWnd, NULL, TRUE);
345
346 #ifdef USER_SOUNDS
347         if (!GetNHApp()->bNoSounds)
348             play_sound_for_message(msg_data->text);
349 #endif
350     } break;
351
352     case MSNH_MSG_CLEAR_WINDOW: {
353         data->lines_last_turn = 0;
354         data->lines_not_seen = 0;
355         data->nevermore = 0;
356         break;
357     }
358     case MSNH_MSG_CARET:
359         /* Create or destroy a caret */
360         if (*(int *) lParam)
361             CreateCaret(hWnd, NULL, 0, data->yChar);
362         else {
363             DestroyCaret();
364             /* this means we just did something interactive in this window, so
365                we
366                don't need a --More-- for the lines above.
367                */
368             data->lines_not_seen = 0;
369         }
370         break;
371
372     case MSNH_MSG_GETTEXT: {
373         PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam;
374         int i;
375         size_t buflen;
376
377         buflen = 0;
378         for (i = 0; i < MSG_LINES; i++)
379             if (*data->window_text[i].text) {
380                 strncpy(&msg_data->buffer[buflen], data->window_text[i].text,
381                         msg_data->max_size - buflen);
382                 buflen += strlen(data->window_text[i].text);
383                 if (buflen >= msg_data->max_size)
384                     break;
385
386                 strncpy(&msg_data->buffer[buflen], "\r\n",
387                         msg_data->max_size - buflen);
388                 buflen += 2;
389                 if (buflen > msg_data->max_size)
390                     break;
391             }
392     } break;
393
394     } /* switch( wParam ) */
395 }
396
397 void
398 onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
399 {
400     PNHMessageWindow data;
401     SCROLLINFO si;
402     int yInc;
403
404     UNREFERENCED_PARAMETER(lParam);
405
406     /* get window data */
407     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
408
409     ZeroMemory(&si, sizeof(si));
410     si.cbSize = sizeof(si);
411     si.fMask = SIF_PAGE | SIF_POS;
412     GetScrollInfo(hWnd, SB_VERT, &si);
413
414     switch (LOWORD(wParam)) {
415     // User clicked the shaft above the scroll box.
416
417     case SB_PAGEUP:
418         yInc = -(int) si.nPage;
419         break;
420
421     // User clicked the shaft below the scroll box.
422
423     case SB_PAGEDOWN:
424         yInc = si.nPage;
425         break;
426
427     // User clicked the top arrow.
428
429     case SB_LINEUP:
430         yInc = -1;
431         break;
432
433     // User clicked the bottom arrow.
434
435     case SB_LINEDOWN:
436         yInc = 1;
437         break;
438
439     // User dragged the scroll box.
440
441     case SB_THUMBTRACK:
442         yInc = HIWORD(wParam) - data->yPos;
443         break;
444
445     default:
446         yInc = 0;
447     }
448
449     // If applying the vertical scrolling increment does not
450     // take the scrolling position out of the scrolling range,
451     // increment the scrolling position, adjust the position
452     // of the scroll box, and update the window. UpdateWindow
453     // sends the WM_PAINT message.
454
455     if (yInc = max(MSG_VISIBLE_LINES - data->yPos,
456                    min(yInc, data->yMax - data->yPos))) {
457         data->yPos += yInc;
458         /* ScrollWindowEx(hWnd, 0, -data->yChar * yInc,
459                 (CONST RECT *) NULL, (CONST RECT *) NULL,
460                 (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE);
461         */
462         InvalidateRect(hWnd, NULL, TRUE);
463
464         ZeroMemory(&si, sizeof(si));
465         si.cbSize = sizeof(si);
466         si.fMask = SIF_POS;
467         si.nPos = data->yPos;
468         SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
469
470         UpdateWindow(hWnd);
471     }
472 }
473
474 #ifndef MSG_WRAP_TEXT
475 void
476 onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
477 {
478     PNHMessageWindow data;
479     SCROLLINFO si;
480     int xInc;
481
482     /* get window data */
483     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
484
485     ZeroMemory(&si, sizeof(si));
486     si.cbSize = sizeof(si);
487     si.fMask = SIF_PAGE;
488     GetScrollInfo(hWnd, SB_HORZ, &si);
489
490     switch (LOWORD(wParam)) {
491     // User clicked shaft left of the scroll box.
492
493     case SB_PAGEUP:
494         xInc = -(int) si.nPage;
495         break;
496
497     // User clicked shaft right of the scroll box.
498
499     case SB_PAGEDOWN:
500         xInc = si.nPage;
501         break;
502
503     // User clicked the left arrow.
504
505     case SB_LINEUP:
506         xInc = -1;
507         break;
508
509     // User clicked the right arrow.
510
511     case SB_LINEDOWN:
512         xInc = 1;
513         break;
514
515     // User dragged the scroll box.
516
517     case SB_THUMBTRACK:
518         xInc = HIWORD(wParam) - data->xPos;
519         break;
520
521     default:
522         xInc = 0;
523     }
524
525     // If applying the horizontal scrolling increment does not
526     // take the scrolling position out of the scrolling range,
527     // increment the scrolling position, adjust the position
528     // of the scroll box, and update the window.
529
530     if (xInc = max(-data->xPos, min(xInc, data->xMax - data->xPos))) {
531         data->xPos += xInc;
532         ScrollWindowEx(hWnd, -data->xChar * xInc, 0, (CONST RECT *) NULL,
533                        (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
534                        SW_INVALIDATE | SW_ERASE);
535
536         ZeroMemory(&si, sizeof(si));
537         si.cbSize = sizeof(si);
538         si.fMask = SIF_POS;
539         si.nPos = data->xPos;
540         SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
541         UpdateWindow(hWnd);
542     }
543 }
544 #endif // MSG_WRAP_TEXT
545
546 COLORREF
547 setMsgTextColor(HDC hdc, int gray)
548 {
549     COLORREF fg, color1, color2;
550     if (gray) {
551         if (message_bg_brush) {
552             color1 = message_bg_color;
553             color2 = message_fg_color;
554         } else {
555             color1 = (COLORREF) GetSysColor(DEFAULT_COLOR_BG_MSG);
556             color2 = (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MSG);
557         }
558         /* Make a "gray" color by taking the average of the individual R,G,B
559            components of two colors. Thanks to Jonathan del Strother */
560         fg = RGB((GetRValue(color1) + GetRValue(color2)) / 2,
561                  (GetGValue(color1) + GetGValue(color2)) / 2,
562                  (GetBValue(color1) + GetBValue(color2)) / 2);
563     } else {
564         fg = message_fg_brush ? message_fg_color
565                               : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MSG);
566     }
567
568     return SetTextColor(hdc, fg);
569 }
570
571 void
572 onPaint(HWND hWnd)
573 {
574     PAINTSTRUCT ps;
575     HDC hdc;
576     PNHMessageWindow data;
577     RECT client_rt, draw_rt;
578     int FirstLine, LastLine;
579     int i, x, y;
580     HGDIOBJ oldFont;
581     TCHAR wbuf[MAXWINDOWTEXT + 2];
582     size_t wlen;
583     COLORREF OldBg, OldFg;
584
585     hdc = BeginPaint(hWnd, &ps);
586
587     OldBg = SetBkColor(
588         hdc, message_bg_brush ? message_bg_color
589                               : (COLORREF) GetSysColor(DEFAULT_COLOR_BG_MSG));
590     OldFg = setMsgTextColor(hdc, 0);
591
592     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
593
594     GetClientRect(hWnd, &client_rt);
595
596     if (!IsRectEmpty(&ps.rcPaint)) {
597         FirstLine = max(
598             0, data->yPos - (client_rt.bottom - ps.rcPaint.top) / data->yChar
599                    + 1);
600         LastLine =
601             min(MSG_LINES - 1,
602                 data->yPos
603                     - (client_rt.bottom - ps.rcPaint.bottom) / data->yChar);
604         y = min(ps.rcPaint.bottom, client_rt.bottom);
605         for (i = LastLine; i >= FirstLine; i--) {
606             x = data->xChar * (2 - data->xPos);
607
608             draw_rt.left = x;
609             draw_rt.right = client_rt.right;
610             draw_rt.top = y - data->yChar;
611             draw_rt.bottom = y;
612
613             oldFont = SelectObject(
614                 hdc, mswin_get_font(NHW_MESSAGE, data->window_text[i].attr,
615                                     hdc, FALSE));
616
617             /* convert to UNICODE */
618             NH_A2W(data->window_text[i].text, wbuf, sizeof(wbuf));
619             wlen = _tcslen(wbuf);
620             setMsgTextColor(hdc, i < (MSG_LINES - data->lines_last_turn));
621 #ifdef MSG_WRAP_TEXT
622             /* Find out how large the bounding rectangle of the text is */
623             DrawText(hdc, wbuf, wlen, &draw_rt,
624                      DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
625             /* move that rectangle up, so that the bottom remains at the same
626              * height */
627             draw_rt.top = y - (draw_rt.bottom - draw_rt.top);
628             draw_rt.bottom = y;
629
630             /* Now really draw it */
631             DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX | DT_WORDBREAK);
632
633             /* Find out the cursor (caret) position */
634             if (i == MSG_LINES - 1) {
635                 int nnum, numfit;
636                 SIZE size;
637                 TCHAR *nbuf;
638                 int nlen;
639
640                 nbuf = wbuf;
641                 nlen = wlen;
642                 while (nlen) {
643                     /* Get the number of characters that fit on the line */
644                     GetTextExtentExPoint(hdc, nbuf, nlen,
645                                          draw_rt.right - draw_rt.left,
646                                          &numfit, NULL, &size);
647                     /* Search back to a space */
648                     nnum = numfit;
649                     if (numfit < nlen) {
650                         while (nnum > 0 && nbuf[nnum] != ' ')
651                             nnum--;
652                         /* If no space found, break wherever */
653                         if (nnum == 0)
654                             nnum = numfit;
655                     }
656                     nbuf += nnum;
657                     nlen -= nnum;
658                     if (*nbuf == ' ') {
659                         nbuf++;
660                         nlen--;
661                     }
662                 }
663                 /* The last size is the size of the last line. Set the caret
664                    there.
665                    This will fail automatically if we don't own the caret
666                    (i.e.,
667                    when not in a question.)
668                  */
669                 SetCaretPos(draw_rt.left + size.cx,
670                             draw_rt.bottom - data->yChar);
671             }
672 #else
673             DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX);
674             SetCaretPos(draw_rt.left + size.cx, draw_rt.bottom - data->yChar);
675 #endif
676             SelectObject(hdc, oldFont);
677             y -= draw_rt.bottom - draw_rt.top;
678         }
679     }
680     SetTextColor(hdc, OldFg);
681     SetBkColor(hdc, OldBg);
682     EndPaint(hWnd, &ps);
683 }
684
685 void
686 onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
687 {
688     PNHMessageWindow data;
689     SIZE dummy;
690
691     UNREFERENCED_PARAMETER(wParam);
692     UNREFERENCED_PARAMETER(lParam);
693
694     /* set window data */
695     data = (PNHMessageWindow) malloc(sizeof(NHMessageWindow));
696     if (!data)
697         panic("out of memory");
698     ZeroMemory(data, sizeof(NHMessageWindow));
699     data->max_text = MAXWINDOWTEXT;
700     SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
701
702     /* re-calculate window size (+ font size) */
703     mswin_message_window_size(hWnd, &dummy);
704 }
705
706 void
707 mswin_message_window_size(HWND hWnd, LPSIZE sz)
708 {
709     HDC hdc;
710     HGDIOBJ saveFont;
711     TEXTMETRIC tm;
712     PNHMessageWindow data;
713     RECT rt, client_rt;
714
715     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
716     if (!data)
717         return;
718
719     /* -- Calculate the font size -- */
720     /* Get the handle to the client area's device context. */
721     hdc = GetDC(hWnd);
722     saveFont =
723         SelectObject(hdc, mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, FALSE));
724
725     /* Extract font dimensions from the text metrics. */
726     GetTextMetrics(hdc, &tm);
727     data->xChar = tm.tmAveCharWidth;
728     data->xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * data->xChar / 2;
729     data->yChar = tm.tmHeight + tm.tmExternalLeading;
730     data->xPage = 1;
731
732     /* Free the device context.  */
733     SelectObject(hdc, saveFont);
734     ReleaseDC(hWnd, hdc);
735
736     /* -- calculate window size -- */
737     GetWindowRect(hWnd, &rt);
738     sz->cx = rt.right - rt.left;
739     sz->cy = rt.bottom - rt.top;
740
741     /* set size to accomodate MSG_VISIBLE_LINES and
742        horizontal scroll bar (difference between window rect and client rect
743        */
744     GetClientRect(hWnd, &client_rt);
745     sz->cy = sz->cy - (client_rt.bottom - client_rt.top)
746              + data->yChar * MSG_VISIBLE_LINES;
747 }
748
749 /* check if text can be appended to the last line without wrapping */
750 BOOL
751 can_append_text(HWND hWnd, int attr, const char *text)
752 {
753     PNHMessageWindow data;
754     char tmptext[MAXWINDOWTEXT + 1];
755     HDC hdc;
756     HGDIOBJ saveFont;
757     RECT draw_rt;
758     BOOL retval = FALSE;
759
760     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
761
762     /* cannot append if lines_not_seen is 0 (beginning of the new turn */
763     if (data->lines_not_seen == 0)
764         return FALSE;
765
766     /* cannot append text with different attrbutes */
767     if (data->window_text[MSG_LINES - 1].attr != attr)
768         return FALSE;
769
770     /* check if the maximum string langth will be exceeded */
771     if (strlen(data->window_text[MSG_LINES - 1].text) + 2
772             + /* space characters */
773             strlen(text) + strlen(MORE)
774         >= MAXWINDOWTEXT)
775         return FALSE;
776
777     /* check if the text is goinf to fin into a single line */
778     strcpy(tmptext, data->window_text[MSG_LINES - 1].text);
779     strcat(tmptext, "  ");
780     strcat(tmptext, text);
781     strcat(tmptext, MORE);
782
783     hdc = GetDC(hWnd);
784     saveFont = SelectObject(
785         hdc,
786         mswin_get_font(NHW_MESSAGE, data->window_text[MSG_LINES - 1].attr,
787                        hdc, FALSE));
788     GetClientRect(hWnd, &draw_rt);
789     draw_rt.bottom = draw_rt.top; /* we only need width for the DrawText */
790     DrawText(hdc, tmptext, strlen(tmptext), &draw_rt,
791              DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
792
793     /* we will check against 1.5 of the font size in order to determine
794        if the text is single-line or not - just to be on the safe size */
795     retval = (draw_rt.bottom - draw_rt.top) < (data->yChar + data->yChar / 2);
796
797     /* free device context */
798     SelectObject(hdc, saveFont);
799     ReleaseDC(hWnd, hdc);
800     return retval;
801 }
802
803 /* check if "--more--" prompt needs to be displayed
804    basically, check if the lines not seen are going to find in the message
805    window
806 */
807 BOOL
808 more_prompt_check(HWND hWnd)
809 {
810     PNHMessageWindow data;
811     HDC hdc;
812     HGDIOBJ saveFont;
813     RECT client_rt, draw_rt;
814     int i;
815     int remaining_height;
816     char tmptext[MAXWINDOWTEXT + 1];
817
818     data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
819
820     if (data->lines_not_seen == 0)
821         return FALSE; /* don't bother checking - nothig to "more" */
822     if (data->lines_not_seen >= MSG_LINES)
823         return TRUE; /* history size exceeded - always more */
824
825     GetClientRect(hWnd, &client_rt);
826     remaining_height = client_rt.bottom - client_rt.top;
827
828     hdc = GetDC(hWnd);
829     saveFont =
830         SelectObject(hdc, mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, FALSE));
831     for (i = 0; i < data->lines_not_seen; i++) {
832         /* we only need width for the DrawText */
833         SetRect(&draw_rt, client_rt.left, client_rt.top, client_rt.right,
834                 client_rt.top);
835         SelectObject(hdc,
836                      mswin_get_font(NHW_MESSAGE,
837                                     data->window_text[MSG_LINES - i - 1].attr,
838                                     hdc, FALSE));
839
840         strcpy(tmptext, data->window_text[MSG_LINES - i - 1].text);
841         if (i == 0)
842             strcat(tmptext, MORE);
843
844         remaining_height -=
845             DrawText(hdc, tmptext, strlen(tmptext), &draw_rt,
846                      DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
847         if (remaining_height <= 0)
848             break;
849     }
850
851     /* free device context */
852     SelectObject(hdc, saveFont);
853     ReleaseDC(hWnd, hdc);
854     return (remaining_height
855             <= 0); /* TRUE if lines_not_seen take more that window height */
856 }