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