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. */
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
17 #define DEFAULT_COLOR_BG_MSG COLOR_WINDOW
18 #define DEFAULT_COLOR_FG_MSG COLOR_WINDOWTEXT
20 #define MORE "--More--"
24 char text[MAXWINDOWTEXT + 1];
27 typedef struct mswin_nethack_message_window {
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
33 int nevermore; /* We want no more --More-- prompts */
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;
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);
51 static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam);
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 */
59 static BOOL more_prompt_check(HWND hWnd);
60 /* check if "--more--" prompt needs to be displayed */
63 extern void play_sound_for_message(const char *str);
67 mswin_init_message_window()
69 static int run_once = 0;
75 register_message_window_class();
79 /* get window position */
80 if (GetNHApp()->bAutoLayout) {
81 SetRect(&rt, 0, 0, 0, 0);
83 mswin_get_window_placement(NHW_MESSAGE, &rt);
87 style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_SIZEBOX;
89 style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL | WS_SIZEBOX;
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 */
106 panic("Cannot create message window");
108 /* Set window caption */
109 SetWindowText(ret, "Messages");
115 register_message_window_class()
118 ZeroMemory(&wcex, sizeof(wcex));
120 wcex.style = CS_NOCLOSE;
121 wcex.lpfnWndProc = (WNDPROC) NHMessageWndProc;
124 wcex.hInstance = GetNHApp()->hApp;
126 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
127 wcex.hbrBackground = message_bg_brush
129 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MSG);
130 wcex.lpszMenuName = NULL;
131 wcex.lpszClassName = szMessageWindowClass;
133 RegisterClass(&wcex);
137 NHMessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
141 onCreate(hWnd, wParam, lParam);
144 case WM_MSNH_COMMAND:
145 onMSNHCommand(hWnd, wParam, lParam);
153 SetFocus(GetNHApp()->hMainWnd);
156 #ifndef MSG_WRAP_TEXT
158 onMSNH_HScroll(hWnd, wParam, lParam);
163 onMSNH_VScroll(hWnd, wParam, lParam);
167 PNHMessageWindow data;
168 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
170 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
177 PNHMessageWindow data;
180 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
182 xNewSize = LOWORD(lParam);
183 yNewSize = HIWORD(lParam);
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);
191 ZeroMemory(&si, sizeof(si));
192 si.cbSize = sizeof(si);
193 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
195 si.nMax = data->max_text;
196 si.nPage = data->xPage;
197 si.nPos = data->xPos;
198 SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
201 data->yMax = MSG_LINES - 1;
202 data->yPos = min(data->yPos, data->yMax);
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);
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);
220 /* redraw window - it does not handle incremental resizing too well */
221 InvalidateRect(hWnd, NULL, TRUE);
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);
233 return DefWindowProc(hWnd, message, wParam, lParam);
239 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
241 PNHMessageWindow data;
243 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
245 case MSNH_MSG_PUTSTR: {
246 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
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,
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';
265 if (can_append_text(hWnd, msg_data->attr, msg_data->text)) {
266 strncat(data->window_text[MSG_LINES - 1].text, " ",
268 - strlen(data->window_text[MSG_LINES - 1].text));
269 strncat(data->window_text[MSG_LINES - 1].text, msg_data->text,
271 - strlen(data->window_text[MSG_LINES - 1].text));
273 /* check for "--more--" */
274 if (!data->nevermore && more_prompt_check(hWnd)) {
279 /* append more prompt and inticate the update */
281 data->window_text[MSG_LINES - 1].text, MORE,
283 - strlen(data->window_text[MSG_LINES - 1].text));
284 InvalidateRect(hWnd, NULL, TRUE);
288 int c = mswin_nhgetch();
306 /* erase the "--more--" prompt */
307 chop = strlen(data->window_text[MSG_LINES - 1].text)
309 data->window_text[MSG_LINES - 1].text[chop] = '\0';
310 data->lines_not_seen = 0;
313 /* check if the string is empty */
314 for (p = data->window_text[MSG_LINES - 1].text;
315 *p && isspace(*p); 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]));
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,
329 data->lines_not_seen++;
330 data->lines_last_turn++;
334 /* reset V-scroll position to display new text */
335 data->yPos = data->yMax;
337 ZeroMemory(&si, sizeof(si));
338 si.cbSize = sizeof(si);
340 si.nPos = data->yPos;
341 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
343 /* update window content */
344 InvalidateRect(hWnd, NULL, TRUE);
347 if (!GetNHApp()->bNoSounds)
348 play_sound_for_message(msg_data->text);
352 case MSNH_MSG_CLEAR_WINDOW: {
353 data->lines_last_turn = 0;
354 data->lines_not_seen = 0;
359 /* Create or destroy a caret */
361 CreateCaret(hWnd, NULL, 0, data->yChar);
364 /* this means we just did something interactive in this window, so
366 don't need a --More-- for the lines above.
368 data->lines_not_seen = 0;
372 case MSNH_MSG_GETTEXT: {
373 PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam;
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)
386 strncpy(&msg_data->buffer[buflen], "\r\n",
387 msg_data->max_size - buflen);
389 if (buflen > msg_data->max_size)
394 } /* switch( wParam ) */
398 onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
400 PNHMessageWindow data;
404 UNREFERENCED_PARAMETER(lParam);
406 /* get window data */
407 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
409 ZeroMemory(&si, sizeof(si));
410 si.cbSize = sizeof(si);
411 si.fMask = SIF_PAGE | SIF_POS;
412 GetScrollInfo(hWnd, SB_VERT, &si);
414 switch (LOWORD(wParam)) {
415 // User clicked the shaft above the scroll box.
418 yInc = -(int) si.nPage;
421 // User clicked the shaft below the scroll box.
427 // User clicked the top arrow.
433 // User clicked the bottom arrow.
439 // User dragged the scroll box.
442 yInc = HIWORD(wParam) - data->yPos;
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.
455 if (yInc = max(MSG_VISIBLE_LINES - data->yPos,
456 min(yInc, data->yMax - data->yPos))) {
458 /* ScrollWindowEx(hWnd, 0, -data->yChar * yInc,
459 (CONST RECT *) NULL, (CONST RECT *) NULL,
460 (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE);
462 InvalidateRect(hWnd, NULL, TRUE);
464 ZeroMemory(&si, sizeof(si));
465 si.cbSize = sizeof(si);
467 si.nPos = data->yPos;
468 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
474 #ifndef MSG_WRAP_TEXT
476 onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
478 PNHMessageWindow data;
482 /* get window data */
483 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
485 ZeroMemory(&si, sizeof(si));
486 si.cbSize = sizeof(si);
488 GetScrollInfo(hWnd, SB_HORZ, &si);
490 switch (LOWORD(wParam)) {
491 // User clicked shaft left of the scroll box.
494 xInc = -(int) si.nPage;
497 // User clicked shaft right of the scroll box.
503 // User clicked the left arrow.
509 // User clicked the right arrow.
515 // User dragged the scroll box.
518 xInc = HIWORD(wParam) - data->xPos;
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.
530 if (xInc = max(-data->xPos, min(xInc, data->xMax - data->xPos))) {
532 ScrollWindowEx(hWnd, -data->xChar * xInc, 0, (CONST RECT *) NULL,
533 (CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
534 SW_INVALIDATE | SW_ERASE);
536 ZeroMemory(&si, sizeof(si));
537 si.cbSize = sizeof(si);
539 si.nPos = data->xPos;
540 SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
544 #endif // MSG_WRAP_TEXT
547 setMsgTextColor(HDC hdc, int gray)
549 COLORREF fg, color1, color2;
551 if (message_bg_brush) {
552 color1 = message_bg_color;
553 color2 = message_fg_color;
555 color1 = (COLORREF) GetSysColor(DEFAULT_COLOR_BG_MSG);
556 color2 = (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MSG);
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);
564 fg = message_fg_brush ? message_fg_color
565 : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MSG);
568 return SetTextColor(hdc, fg);
576 PNHMessageWindow data;
577 RECT client_rt, draw_rt;
578 int FirstLine, LastLine;
581 TCHAR wbuf[MAXWINDOWTEXT + 2];
583 COLORREF OldBg, OldFg;
585 hdc = BeginPaint(hWnd, &ps);
588 hdc, message_bg_brush ? message_bg_color
589 : (COLORREF) GetSysColor(DEFAULT_COLOR_BG_MSG));
590 OldFg = setMsgTextColor(hdc, 0);
592 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
594 GetClientRect(hWnd, &client_rt);
596 if (!IsRectEmpty(&ps.rcPaint)) {
598 0, data->yPos - (client_rt.bottom - ps.rcPaint.top) / data->yChar
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);
609 draw_rt.right = client_rt.right;
610 draw_rt.top = y - data->yChar;
613 oldFont = SelectObject(
614 hdc, mswin_get_font(NHW_MESSAGE, data->window_text[i].attr,
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));
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
627 draw_rt.top = y - (draw_rt.bottom - draw_rt.top);
630 /* Now really draw it */
631 DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX | DT_WORDBREAK);
633 /* Find out the cursor (caret) position */
634 if (i == MSG_LINES - 1) {
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 */
650 while (nnum > 0 && nbuf[nnum] != ' ')
652 /* If no space found, break wherever */
663 /* The last size is the size of the last line. Set the caret
665 This will fail automatically if we don't own the caret
667 when not in a question.)
669 SetCaretPos(draw_rt.left + size.cx,
670 draw_rt.bottom - data->yChar);
673 DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX);
674 SetCaretPos(draw_rt.left + size.cx, draw_rt.bottom - data->yChar);
676 SelectObject(hdc, oldFont);
677 y -= draw_rt.bottom - draw_rt.top;
680 SetTextColor(hdc, OldFg);
681 SetBkColor(hdc, OldBg);
686 onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
688 PNHMessageWindow data;
691 UNREFERENCED_PARAMETER(wParam);
692 UNREFERENCED_PARAMETER(lParam);
694 /* set window data */
695 data = (PNHMessageWindow) malloc(sizeof(NHMessageWindow));
697 panic("out of memory");
698 ZeroMemory(data, sizeof(NHMessageWindow));
699 data->max_text = MAXWINDOWTEXT;
700 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
702 /* re-calculate window size (+ font size) */
703 mswin_message_window_size(hWnd, &dummy);
707 mswin_message_window_size(HWND hWnd, LPSIZE sz)
712 PNHMessageWindow data;
715 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
719 /* -- Calculate the font size -- */
720 /* Get the handle to the client area's device context. */
723 SelectObject(hdc, mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, FALSE));
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;
732 /* Free the device context. */
733 SelectObject(hdc, saveFont);
734 ReleaseDC(hWnd, hdc);
736 /* -- calculate window size -- */
737 GetWindowRect(hWnd, &rt);
738 sz->cx = rt.right - rt.left;
739 sz->cy = rt.bottom - rt.top;
741 /* set size to accomodate MSG_VISIBLE_LINES and
742 horizontal scroll bar (difference between window rect and client rect
744 GetClientRect(hWnd, &client_rt);
745 sz->cy = sz->cy - (client_rt.bottom - client_rt.top)
746 + data->yChar * MSG_VISIBLE_LINES;
749 /* check if text can be appended to the last line without wrapping */
751 can_append_text(HWND hWnd, int attr, const char *text)
753 PNHMessageWindow data;
754 char tmptext[MAXWINDOWTEXT + 1];
760 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
762 /* cannot append if lines_not_seen is 0 (beginning of the new turn */
763 if (data->lines_not_seen == 0)
766 /* cannot append text with different attrbutes */
767 if (data->window_text[MSG_LINES - 1].attr != attr)
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)
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);
784 saveFont = SelectObject(
786 mswin_get_font(NHW_MESSAGE, data->window_text[MSG_LINES - 1].attr,
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);
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);
797 /* free device context */
798 SelectObject(hdc, saveFont);
799 ReleaseDC(hWnd, hdc);
803 /* check if "--more--" prompt needs to be displayed
804 basically, check if the lines not seen are going to find in the message
808 more_prompt_check(HWND hWnd)
810 PNHMessageWindow data;
813 RECT client_rt, draw_rt;
815 int remaining_height;
816 char tmptext[MAXWINDOWTEXT + 1];
818 data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
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 */
825 GetClientRect(hWnd, &client_rt);
826 remaining_height = client_rt.bottom - client_rt.top;
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,
836 mswin_get_font(NHW_MESSAGE,
837 data->window_text[MSG_LINES - i - 1].attr,
840 strcpy(tmptext, data->window_text[MSG_LINES - i - 1].text);
842 strcat(tmptext, MORE);
845 DrawText(hdc, tmptext, strlen(tmptext), &draw_rt,
846 DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
847 if (remaining_height <= 0)
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 */