OSDN Git Service

add copyright
[nethackexpress/trunk.git] / win / win32 / mhmsgwnd.c
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
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
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     
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
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
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
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
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
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
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
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 }