1 /* Copyright (C) 2003 by Alex Kompel <shurikk@pacbell.net> */
2 /* NetHack may be freely redistributed.  See license for details. */
3 
4 #include "mhtxtbuf.h"
5 
6 /* Collect Nethack text messages and render text into edit box.
7    Wrap text if necessary.
8    Recognize formatted lines as having more that 4 consecutive.
9    spaces inside the string.
10    Strip leading and trailing spaces.
11    Always break at the original line end (do not merge text that comes
12    from NetHack engine)
13 */
14 
15 /*----------------------------------------------------------------*/
16 #define NHTEXT_BUFFER_INCREMENT 10
17 /*----------------------------------------------------------------*/
18 struct text_buffer_line {
19 	int		attr;
20 	short	beg_padding;
21 	short	end_padding;
22 	BOOL	formatted;
23 	char*   text;
24 };
25 /*----------------------------------------------------------------*/
26 typedef struct mswin_nethack_text_buffer {
27 	BOOL b_wrap_text;
28 	int n_size;
29 	int n_used;
30 	struct text_buffer_line *text_buffer_line;
31 } NHTextBuffer, *PNHTextBuffer;
32 /*----------------------------------------------------------------*/
33 #define NHTextLine(pb,i)  ((pb)->text_buffer_line[(i)])
34 static TCHAR* nh_append( TCHAR* s, int* size, const char* ap );
35 /*----------------------------------------------------------------*/
mswin_init_text_buffer(BOOL wrap_text)36 PNHTextBuffer mswin_init_text_buffer(BOOL wrap_text)
37 {
38 	PNHTextBuffer pb = (PNHTextBuffer)malloc(sizeof(NHTextBuffer));
39 	if( !pb ) panic("Out of memory");
40 
41 	ZeroMemory(pb, sizeof(NHTextBuffer));
42 	pb->b_wrap_text = wrap_text;
43 	pb->n_size = 0;
44 	pb->n_used = 0;
45 	pb->text_buffer_line = NULL;
46 	return pb;
47 }
48 /*----------------------------------------------------------------*/
mswin_free_text_buffer(PNHTextBuffer pb)49 void mswin_free_text_buffer(PNHTextBuffer pb)
50 {
51 	int i;
52 
53 	if( !pb ) return;
54 
55 	for(i=0; i<pb->n_used; i++ ) {
56 		free(pb->text_buffer_line[i].text);
57 	}
58 	free( pb->text_buffer_line );
59 	free( pb );
60 }
61 /*----------------------------------------------------------------*/
mswin_add_text(PNHTextBuffer pb,int attr,const char * text)62 void mswin_add_text(PNHTextBuffer pb, int attr, const char* text)
63 {
64 	char* p;
65 	struct text_buffer_line* new_line;
66 
67 	/* grow buffer */
68 	if( pb->n_used >= pb->n_size ) {
69 		pb->n_size += NHTEXT_BUFFER_INCREMENT;
70 		pb->text_buffer_line =
71 			(struct text_buffer_line*)realloc( pb->text_buffer_line, pb->n_size*sizeof(struct text_buffer_line) );
72 		if( !pb->text_buffer_line ) panic("Memory allocation error");
73 	}
74 
75 	/* analyze the new line of text */
76 	new_line = &NHTextLine(pb, pb->n_used);
77 	new_line->attr = attr;
78 	new_line->beg_padding = 0;
79 	new_line->text = strdup(text);
80 	for( p = new_line->text; *p && isspace(*p); p++ ) {
81 		new_line->beg_padding++;
82 	}
83 	if( *p ) {
84 		memmove(new_line->text,
85 				new_line->text + new_line->beg_padding,
86 				strlen(new_line->text) - new_line->beg_padding + 1
87 				);
88 		for( p = new_line->text+strlen(new_line->text);
89 			 p>=new_line->text && isspace(*p);
90 			 p-- ) {
91 			new_line->end_padding++;
92 			*p = 0;
93 		}
94 
95 		/* if there are 3 (or more) consecutive spaces inside the string
96 		   consider it formatted */
97 		new_line->formatted = (strstr(new_line->text, "   ")!=NULL);
98 	} else {
99 		new_line->end_padding = 0;
100 		new_line->text[0] = 0;
101 		new_line->formatted = FALSE;
102 	}
103 	pb->n_used++;
104 }
105 /*----------------------------------------------------------------*/
nh_append(TCHAR * s,int * size,const char * ap)106 static TCHAR* nh_append( TCHAR* s, int* size, const char* ap )
107 {
108 	int tlen, tnewlen;
109 
110 	if( !(ap && *ap) ) return s;
111 
112 	/* append the calculated line to the text buffer */
113 	tlen = s? _tcslen(s) : 0;
114 	tnewlen = tlen+strlen(ap);
115 	if( tnewlen>=*size ) {
116 		*size = max(tnewlen, *size + BUFSZ);
117 		s = (TCHAR*)realloc(s, *size * sizeof(TCHAR));
118 		if( !s ) panic("Out of memory");
119 		ZeroMemory(s+tlen, (*size-tlen)*sizeof(TCHAR));
120 	}
121 	if( strcmp(ap, "\r\n")==0 ) {
122 		_tcscat(s, TEXT("\r\n"));
123 	} else {
124 		NH_A2W(ap, s+tlen, strlen(ap));
125 		s[tnewlen] = 0;
126 	}
127 	return s;
128 }
129 /*----------------------------------------------------------------*/
mswin_render_text(PNHTextBuffer pb,HWND edit_control)130 void mswin_render_text(PNHTextBuffer pb, HWND edit_control)
131 {
132 	RECT rt_client;      /* boundaries of the client area of the edit control */
133 	SIZE size_text;      /* size of the edit control */
134 	RECT rt_text;        /* calculated text rectangle for the visible line */
135 	char buf[BUFSZ];     /* buffer for the visible line */
136 	TCHAR tbuf[BUFSZ];   /* temp buffer for DrawText */
137 	TCHAR* pText = NULL; /* resulting text (formatted) */
138 	int pTextSize = 0;   /* resulting text size */
139 	char* p_cur = NULL;  /* current position in the NHTextBuffer->text_buffer_line->text */
140 	char* p_buf_cur = NULL; /* current position in the visible line buffer */
141 	int i;
142 	HDC hdcEdit;		 /* device context for the edit control */
143 	HFONT hFont, hOldFont; /* edit control font */
144 
145 	GetClientRect(edit_control, &rt_client );
146 	size_text.cx = rt_client.right - rt_client.left;
147 	size_text.cy = rt_client.bottom - rt_client.top;
148 	size_text.cx -= GetSystemMetrics(SM_CXVSCROLL); /* add a slight right margin - the text looks better that way */
149 	hdcEdit = GetDC(edit_control);
150 	hFont = (HFONT)SendMessage(edit_control, WM_GETFONT, 0, 0);
151 	if( hFont ) hOldFont = SelectObject(hdcEdit, hFont);
152 
153 	/* loop through each line (outer loop) and wrap it around (inner loop) */
154 	ZeroMemory(buf, sizeof(buf));
155 	p_buf_cur = buf;
156 	for( i=0; i<pb->n_used; i++ ) {
157 		if( pb->b_wrap_text ) {
158 			p_cur = NHTextLine(pb,i).text;
159 
160 			/* insert an line break for the empty string */
161 			if( !NHTextLine(pb,i).text[0] ) {
162 				pText = nh_append(pText, &pTextSize, "\r\n");
163 				continue;
164 			}
165 
166 			/* add margin to the "formatted" line of text */
167 			if( NHTextLine(pb,i).formatted ) {
168 				strcpy(buf, "   ");
169 				p_buf_cur += 3;
170 			}
171 
172 			/* scroll thourgh the current line of text and wrap it
173 			   so it fits to width of the edit control */
174 			while( *p_cur ) {
175 				char *p_word_pos = p_buf_cur;
176 
177 				/* copy one word into the buffer */
178 				while( *p_cur && isspace(*p_cur) )
179 					if( p_buf_cur!=buf ) *p_buf_cur++ = *p_cur++;
180 					else p_cur++;
181 
182 				while( *p_cur && !isspace(*p_cur) )
183 					*p_buf_cur++ = *p_cur++;
184 
185 				/* check if it fits */
186 				SetRect( &rt_text, 0, 0, size_text.cx, size_text.cy );
187 				DrawText(hdcEdit, NH_A2W(buf, tbuf, p_buf_cur-buf), p_buf_cur-buf, &rt_text, DT_CALCRECT | DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
188 				if( (rt_text.right - rt_text.left)>=size_text.cx ) {
189 					/* Backtrack.
190 					   Only backtrack if the last word caused the overflow -
191 					   do not backtrack if the entire current line does not fit the visible area.
192 					   Otherwise it is a infinite loop.
193 					*/
194 					if( p_word_pos>buf ) {
195 						p_cur -= (p_buf_cur-p_word_pos);
196 						p_buf_cur = p_word_pos;
197 					}
198 					*p_buf_cur = 0;  /* break the line */
199 
200 					/* append the calculated line to the text buffer */
201 					pText = nh_append(pText, &pTextSize, buf);
202 					pText = nh_append(pText, &pTextSize, "\r\n");
203 					ZeroMemory(buf, sizeof(buf));
204 					p_buf_cur = buf;
205 				}
206 			}
207 
208 			/* always break the line at the end of the buffer text */
209 			if( p_buf_cur != buf ) {
210 				/* flush the current buffrer */
211 				*p_buf_cur = 0;  /* break the line */
212 				pText = nh_append(pText, &pTextSize, buf);
213 				pText = nh_append(pText, &pTextSize, "\r\n");
214 				ZeroMemory(buf, sizeof(buf));
215 				p_buf_cur = buf;
216 			}
217 		} else { /* do not wrap text */
218 			int j;
219 			for( j=0; j<NHTextLine(pb,i).beg_padding; j++ )
220 				pText = nh_append(pText, &pTextSize, " ");
221 			pText = nh_append(pText, &pTextSize, NHTextLine(pb,i).text);
222 			pText = nh_append(pText, &pTextSize, "\r\n");
223 		}
224 	}
225 
226 	/* cleanup */
227 	if( hFont ) SelectObject(hdcEdit, hOldFont);
228 	ReleaseDC(edit_control, hdcEdit);
229 
230 	/* update edit control text */
231 	if( pText ) {
232 		SendMessage(edit_control, EM_FMTLINES, 1, 0 );
233 		SetWindowText(edit_control, pText);
234 		free(pText);
235 	}
236 }
237 /*----------------------------------------------------------------*/
238 
239