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