1//note: if you reuse a particular wordwrap struct, you have to free frame->wrapbox.bufferarray to avoid a memory leak,
2//and strictly speaking to free it before program exit.
3static int _zz_wordwrap
(char *src,
ALLEGRO_FONT *font,
int pixelwidth, ZZ_WRAPBOX
*wrapbox
)
4{
5 char *current_line
= src
; //allow zero based indices within current line
6 char *current_dst
;
7 char *dst_resize
;
8 char *wrapbuff
; //temp store for current line
9 int dst_current_char
= 0; //index of current line in dest
10 int src_prev_space
= 0; //marks last space in source (if we have to ignore part of a word at end of line)
11 int dst_prev_space
= 0; //marks last space in dest
12 int src_current_char
= 0; //how far within the current string we are
13 int dst_lines_used
= 0; //check that we don't overrun the destination buffer
14 int num_dst_lines
; //how many lines in dst available (chars_width * num_dest_lines = number of bytes in dst buffer)
15 int num_src_bytes
;
16 char c
;
17
18 memset(wrapbox,
0,
sizeof(ZZ_WRAPBOX
));
19 wrapbox->textfont
= font;
20 wrapbox->fontheight
= al_get_font_line_height(font);
21 wrapbox->pixelwidth
= pixelwidth
;
22
23 wrapbox->chars_width
= al_get_text_width(font, ten_narrowchars
);
24 wrapbox->chars_width
= (float)(10.
0 / ((float)wrapbox->chars_width
/ (float)pixelwidth
));
25
26 wrapbuff
= (char *)malloc(wrapbox->chars_width
+ 1);
27 if(!wrapbuff
)
28 {
29 return ZZ_WRAP_NOMEM
;
30 }
31
32 //valgrind complains about conditional jump on uninitialized values if this isn't here, remove if you like
33 memset(wrapbuff,
0,wrapbox->chars_width
+ 1);
34
35 //allocating src_bytes worth of dst plus a WAG of 20% for slop
36 num_src_bytes
= strlen(src
);
37 num_dst_lines
= num_src_bytes
/ wrapbox->chars_width
; //first approximation to how many lines
38 num_dst_lines
= (float)num_dst_lines
* 1.
2; //WAG of 20% guessing most lines waste that much on the end
39 num_dst_lines
+= 10; //for those one-liners ;)
40 current_dst
= (char *)malloc(num_dst_lines
* wrapbox->chars_width
);
41 if(!current_dst
)
42 {
43 return ZZ_WRAP_NOMEM
;
44 }
45
46 memset(current_dst,
0, num_dst_lines
* wrapbox->chars_width
); //zero them all out for asciiz terminators
47
48 while(1)
49 {
50 c
= current_line
[src_current_char
]; //grab next char from source
51
52 if(c
== 0) //null terminator? done
53 {
54 strcpy(¤t_dst
[dst_lines_used
* wrapbox->chars_width
], wrapbuff
); //copy temp to dest buffer
55 free(wrapbuff
);
56 wrapbox->numdestlines
= dst_lines_used
+1; //tell caller how many lines of wrapped text there are
57
58 wrapbox->bufferarray
= current_dst
;
59 return 0;
60 }
61
62 if(c
== '\n') //start a new line in dest buffer regardless of room remaining in this line
63 {
64 wrapbox->linetoolong
++;
65 strcpy(¤t_dst
[dst_lines_used
* wrapbox->chars_width
], wrapbuff
); //copy temp to dest buffer
66 memset(wrapbuff,
0, wrapbox->chars_width
+ 1); //zero out temp for asciiz terminators
67 dst_lines_used
++;
68 if(dst_lines_used
== num_dst_lines
) //need destination buffer enlarged?
69 {
70 /* trying out Peter Wang suggestion of doubling size every realloc()
71 static int lcount = 1;
72 printf("doubling line count %d times\n",lcount);
73 lcount++;
74 */
75 num_dst_lines
*= 2; //+= 50; //add 50 more lines available
76 dst_resize
= (char *)realloc(current_dst, num_dst_lines
* wrapbox->chars_width
);
77 if(!dst_resize
)
78 {
79 free(wrapbuff
);
80 free(current_dst
);
81 return ZZ_WRAP_NOMEM
;
82 }
83 current_dst
= dst_resize
;
84 }
85 current_line
= ¤t_line
[src_current_char
+ 1]; //+1 to get past the '\n'
86 src_current_char
= -1;
87 src_prev_space
= 0;
88 dst_prev_space
= 0;
89 dst_current_char
= 0;
90 }
91
92 if(c
< ' ') //ignore backspaces, tabs, '\r's, control chars
93 {
94 src_current_char
++; //get past the '\r' or whatever
95 continue; //since c is signed, all the "high" non-ASCII chars will be less than ' ' too
96 }
97
98 if(c
== ' ') //keep track of possible line end (wrapbuff full part way through a word)
99 {
100 src_prev_space
= src_current_char
+ 1; //+1 to ignore it next line
101 dst_prev_space
= dst_current_char
;
102 }
103
104 //fall through to storing in wrapbuff
105 if( (wrapbox->chars_width
== (dst_current_char
+ 1)) || (wrapbox->pixelwidth
< al_get_text_width(font, wrapbuff
))) //run out of room in this line?
106 {
107 if(dst_prev_space
== 0) //we found one single word that's too long for a line
108 {
109 free(wrapbuff
);
110 free(current_dst
);
111 return ZZ_WRAP_TOOLONG
;
112 }
113
114 if(c
!= ' ') //part way through a word?
115 {
116 dst_current_char
= dst_prev_space
;
117 src_current_char
= src_prev_space
;
118 while(wrapbuff
[dst_current_char
] == ' ')
119 {
120 wrapbuff
[dst_current_char
] = 0;
121 dst_current_char--
;
122 if(dst_current_char
< 0)
123 {
124 break;
125 }
126 }
127 }
128
129 strcpy(¤t_dst
[dst_lines_used
* wrapbox->chars_width
], wrapbuff
); //copy temp to dest buffer
130 memset(wrapbuff,
0, wrapbox->chars_width
+ 1); //zero out temp for asciiz terminators
131 dst_lines_used
++;
132 if(dst_lines_used
== num_dst_lines
) //need destination buffer enlarged?
133 {
134 num_dst_lines
+= 50; //add 50 more lines available
135 dst_resize
= (char *)realloc(current_dst, num_dst_lines
* wrapbox->chars_width
);
136 if(!dst_resize
)
137 {
138 free(wrapbuff
);
139 free(current_dst
);
140 return ZZ_WRAP_NOMEM
;
141 }
142 current_dst
= dst_resize
;
143 }
144 current_line
= ¤t_line
[src_prev_space
];
145 src_current_char
= 0;
146 dst_prev_space
= 0;
147 dst_current_char
= 0;
148 continue;
149 }
150 wrapbuff
[dst_current_char
] = c
;
151 src_current_char
++;
152 dst_current_char
++;
153 }
154 return 0;
155}
156
157static void _zz_init_frame
(int left,
int top,
int right,
int bottom,
158 char *titletext,
ALLEGRO_FONT *titlefont,
159 ZZ_TEXTBOX
*frame, ZZ_WIN
*window)
160{
161 int height, width
;
162 frame->framerect.left
= left
;
163 frame->framerect.top
= top
;
164 frame->framerect.right
= right
;
165
166 frame->textbox.left
= frame->framerect.left
+ TEXTBOXMARGIN
;
167 frame->textbox.top
= frame->framerect.top
+ TEXTBOXMARGIN
;
168 frame->textbox.right
= frame->framerect.right
- TEXTBOXMARGIN
;
169
170 frame->titletext
= titletext
;
171 frame->titlefont
= titlefont
;
172 frame->window
= window;
173
174 height
= al_get_font_line_height(titlefont
);
175 width
= al_get_text_width(titlefont, titletext
);
176
177 frame->titlebox.top
= frame->framerect.top
- (height
>> 1);
178 frame->titlebox.bottom
= frame->titlebox.top
+ height
;
179 frame->titlebox.left
= frame->framerect.left
+ 10;
180 frame->titlebox.right
= frame->titlebox.left
+ width
+ 10;
181
182 //leave zero in the "bottom" parameter to have bottom automagically
183 //align with bottom of text
184 if(bottom
!= 0)
185 {
186 frame->framerect.bottom
= bottom
;
187 frame->textbox.bottom
= frame->framerect.bottom
- TEXTBOXMARGIN
;
188 }
189 else
190 {
191 frame->textbox.bottom
= frame->titlebox.bottom
+ (frame->wrapbox.numdestlines
* frame->wrapbox.fontheight
);
192 frame->framerect.bottom
= frame->textbox.bottom
+ TEXTBOXMARGIN
;
193 }
194}
195
196int zz_init_textbox
(ZZ_TEXTBOX
*textbox,
//textbox struct location
197 ZZ_WIN
*parentwin,
//parent window struct location
198 ALLEGRO_FONT *titlefont,
//font for title (may be 0 if no title desired)
199 ALLEGRO_FONT *bodyfont,
//font for body of text (may be 0 if no body desired)
200 char *titletext,
//pointer to text for title (may be 0 if no title desired)
201 char *body_text_src,
//pointer to original cstring for body of text (may be 0 if no body desired)
202 int left,
//left edge on parent window
203 int top,
//top edge on parent window
204 int right,
//right edge on parent window
205 int bottom,
//bottom edge on parent window (may be 0 if you want bottom set to end of body of text)
206 int gray
)
207{
208 int err
;
209
210 err
= _zz_wordwrap
(body_text_src, bodyfont, right-left-TEXTBOXMARGIN
*2,
&textbox->wrapbox
);
211 if(err
)
212 {
213 return err
;
214 }
215
216 _zz_init_frame
(left, top, right, bottom,
//was _zz_init_textbox(?)
217 titletext, titlefont,
218 textbox, parentwin
);
219 textbox->gray
= gray
;
220 return 0;
221}
222
223void zz_draw_textbox
(ZZ_TEXTBOX
*frame
)
224{
225 int x
;
226 int y
;
227 int width
;
228 int height
;
229
230 int row
;
231 int limit
;
232 int indx
;
233 int i
;
234 int gray
;
235
236 if(frame->gray
== 0) //force default
237 gray
= UNSELECTED
; //force washed out appearance
238 else
239 gray
= frame->window->grayout
;
240
241 x
= frame->framerect.left
+ frame->window->rect.left
;
242 y
= frame->framerect.top
+ frame->window->rect.top
;
243 width
= frame->framerect.right
- frame->framerect.left
;
244 height
= frame->framerect.bottom
- frame->framerect.top
;
245
246 //draw outline
247
248 //top line (tapered at the ends)
249 al_draw_line(x
- 0.
5,
250 y
- 0.
5,
251 x
+ width
+ 0.
5,
252 y
- 0.
5,
253 zz_gui_colors
[gray
][DIMGRAY
],
1.
0);
254
255 al_draw_line(x
+ 0.
5,
256 y
+ 0.
5,
257 x
+ width
- 0.
5,
258 y
+ 0.
5,
259 zz_gui_colors
[gray
][BRIGHTGRAY
],
1.
0);
260
261 //bottom line
262 al_draw_line(x
+ 0.
5,
263 height
+ y
- 0.
5,
264 x
+ width
- 0.
5,
265 height
+ y
- 0.
5,
266 zz_gui_colors
[gray
][DIMGRAY
],
1.
0);
267
268 al_draw_line(x
- 0.
5,
269 height
+ y
+ 0.
5,
270 x
+ width
+ 0.
5,
271 height
+ y
+ 0.
5,
272 zz_gui_colors
[gray
][BRIGHTGRAY
],
1.
0);
273
274 //left line
275 al_draw_line(x
- 0.
5,
276 y
- 0.
5,
277 x
- 0.
5,
278 y
+ height
+ 0.
5,
279 zz_gui_colors
[gray
][DARKGRAY
],
1.
0);
280
281 al_draw_line(x
+ 0.
5,
282 y
- 0.
5,
283 x
+ 0.
5,
284 y
+ height
+ 0.
5,
285 zz_gui_colors
[gray
][LIGHTGRAY
],
1.
0);
286
287 //right line
288 al_draw_line(x
+ width
- 0.
5,
289 y
+ 0.
5,
290 x
+ width
- 0.
5,
291 height
+ y
- 0.
5,
292 zz_gui_colors
[gray
][DARKGRAY
],
1.
0);
293
294 al_draw_line(x
+ width
+ 0.
5,
295 y
- 0.
5,
296 x
+ width
+ 0.
5,
297 height
+ y
+ 0.
5,
298 zz_gui_colors
[gray
][LIGHTGRAY
],
1.
0);
299
300 if(frame->titletext
)
301 {
302 al_draw_filled_rectangle(frame->window->rect.left
+ frame->titlebox.left,
303 frame->window->rect.top
+ frame->titlebox.top,
304 frame->window->rect.left
+ frame->titlebox.right,
305 frame->window->rect.top
+ frame->titlebox.bottom,
306 zz_gui_colors
[gray
][GRAY
]);
307
308 al_draw_text(frame->titlefont, zz_gui_colors
[gray
][BLACK
],
(float) frame->titlebox.left
+ frame->window->rect.left
+ 5,
309 (float) frame->titlebox.top
+ frame->window->rect.top,
0, frame->titletext
);
310 }
311
312 if(frame->wrapbox.bufferarray
)
313 {
314 row
= frame->window->rect.top
+ frame->textbox.top
;
315 width
= frame->wrapbox.chars_width
;
316 limit
= frame->wrapbox.numdestlines
;
317
318 for(indx
= 0, i
= 0; i
< limit
;indx
+=width,i
++)
319 {
320 al_draw_text(frame->wrapbox.textfont,
321 zz_gui_colors
[gray
][BLACK
],
322 frame->window->rect.left
+ frame->textbox.left,
323 row,
324 0,
325 &frame->wrapbox.bufferarray
[indx
]);
326 row
+=frame->wrapbox.fontheight
;
327 }
328 }
329}
330
331int zz_init_scrolltext
( ZZ_SCROLLTEXT
*textbox,
//textbox struct location
332 ZZ_WIN
*parentwin,
//parent window struct location
333 ALLEGRO_FONT *scrollfont,
//font for body of text (may be 0 if no body desired)
334 char *body_text_src,
//pointer to original cstring for body of text (may be 0 if no body desired)
335 int left,
//left edge on parent window
336 int top,
//top edge on parent window
337 int right,
//right edge on parent window
338 int bottom,
//bottom edge on parent window (will be rounded from 0.5 lines of text to provide vertical alignment)
339 int scrollwidth,
//how wide the strings can be in pixels (horizontal scroll necessary if wider than right-left)
340 int gray
)
341{
342 int err
;
343 float rnder
;
344 err
= _zz_wordwrap
(body_text_src, scrollfont, scrollwidth,
&textbox->wrapbox
);
345 if(err
)
346 {
347 return err
;
348 }
349 textbox->parent
= parentwin
;
350 textbox->boxwidth
= right-left
;
351 textbox->scrollrect.parent
= parentwin
;
352 textbox->scrollrect.rect.left
= left
;
353 textbox->scrollrect.rect.top
= top
;
354 textbox->scrollrect.rect.right
= right
;
355 rnder
= bottom
- top
;
356 rnder
/= textbox->wrapbox.fontheight
;
357 textbox->scrollrect.rect.bottom
= rnder
;
358 textbox->scrollrect.rect.bottom
*= textbox->wrapbox.fontheight
;
359 textbox->gray
= gray
;
360 err
= (textbox->scrollrect.rect.bottom
- top
)/textbox->wrapbox.fontheight
;
361 zz_init_scrollbar
((textbox->scrollrect.rect.bottom
- top
)/textbox->wrapbox.fontheight,textbox->wrapbox.numdestlines,ZZ_VERTICAL,
&textbox->vscroll,
&textbox->scrollrect
);
362 return 0;
363}
364
365void zz_do_scrolltext
(ZZ_SCROLLTEXT
*textbox
)
366{
367 int top
;
368 int len
;
369 int i
;
370 int line;
371 int hgt
;
372 zz_do_scrollbar
(&textbox->vscroll
);
373 top
= textbox->vscroll.topline
;
374 len
= textbox->vscroll.items_in_window
+ top
;
375 hgt
= textbox->wrapbox.fontheight
;
376 line = textbox->scrollrect.rect.top
;
377 al_draw_filled_rectangle(textbox->scrollrect.rect.left,textbox->scrollrect.rect.top,textbox->scrollrect.rect.right,textbox->scrollrect.rect.bottom,zz_gui_colors
[SELECTED
][WHITE
]);
378 zz_bevel
(textbox->scrollrect.rect.left,
379 textbox->scrollrect.rect.top,
380 textbox->scrollrect.rect.right,
381 textbox->scrollrect.rect.bottom,
382 1,
383 textbox->parent,
384 1);
385 for(i
=top
;i
<len
;i
++)
386 {
387 al_draw_text(textbox->wrapbox.textfont,zz_gui_colors
[SELECTED
][BLACK
], textbox->scrollrect.rect.left,
line,
0,
&textbox->wrapbox.bufferarray
[i
* textbox->wrapbox.chars_width
]);
388 line += hgt
;
389 }
390}