1 /*
2  *
3  *   Copyright (c) 2004-2007 Arthur Huillet
4  *   Copyright (c) 1994, 2002, 2003 Johannes Prix
5  *   Copyright (c) 1994, 2002 Reinhard Prix
6  *
7  *
8  *  This file is part of Freedroid
9  *
10  *  Freedroid is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  Freedroid is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with Freedroid; see the file COPYING. If not, write to the
22  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  *  MA  02111-1307  USA
24  *
25  */
26 
27 /**
28  * This file contains all functions dealing with texts on the screen,
29  * that have to be blitted somehow, using bitmaps or OpenGL texturers,
30  * but at least strongly rely on graphics concepts, not pure internal
31  * text-processing.
32  */
33 
34 #define _text_c
35 
36 #include "system.h"
37 
38 #include "defs.h"
39 #include "struct.h"
40 #include "proto.h"
41 #include "global.h"
42 
43 #include "lvledit/lvledit_display.h"
44 
45 // current text insertion position
46 static int MyCursorX;
47 static int MyCursorY;
48 
49 static int display_char_disabled;
50 
51 /**
52  * This function determines how many lines of text it takes to write a string
53  * with the current font into the given rectangle, provided one would start at
54  * the left side.
55  */
get_lines_needed(const char * text,SDL_Rect t_rect,float line_height_factor)56 int get_lines_needed(const char *text, SDL_Rect t_rect, float line_height_factor)
57 {
58 	t_rect.h = 32767; // arbitrary large number
59 	display_char_disabled = TRUE;
60 	int lines = display_text(text, t_rect.x, t_rect.y, &t_rect, line_height_factor);
61 	display_char_disabled = FALSE;
62 	return lines;
63 }
64 
65 /**
66  *
67  *
68  */
show_backgrounded_label_at_pixel_position(char * LabelText,int pos_x,int pos_y)69 void show_backgrounded_label_at_pixel_position(char *LabelText, int pos_x, int pos_y)
70 {
71 	SDL_Rect background_rect;
72 
73 	background_rect.x = pos_x - 1;
74 	background_rect.y = pos_y;
75 	background_rect.w = text_width(get_current_font(), LabelText) + 2;
76 	background_rect.h = 20;
77 
78 	draw_rectangle(&background_rect, 0, 0, 0, 255);
79 
80 	set_current_font(FPS_Display_Font);
81 	put_string(get_current_font(), pos_x, pos_y, LabelText);
82 
83 };				// void show_backgrounded_label_at_pixel_position ( char* LabelText , float fill_status , int pos_x , int pos_y )
84 
85 /**
86  *
87  *
88  */
show_backgrounded_label_at_map_position(char * LabelText,float fill_status,float pos_x,float pos_y,int zoom_is_on)89 void show_backgrounded_label_at_map_position(char *LabelText, float fill_status, float pos_x, float pos_y, int zoom_is_on)
90 {
91 	int pixel_x, pixel_y;
92 
93 	translate_map_point_to_screen_pixel(pos_x, pos_y, &pixel_x, &pixel_y);
94 	show_backgrounded_label_at_pixel_position(LabelText, pixel_x, pixel_y);
95 
96 };				// void show_backgrounded_label_at_map_position ( char* LabelText , float fill_status , float pos_x , float pos_y )
97 
98 #define IN_WINDOW_TEXT_OFFSET 15
99 
100 /**
101  * Show text inside a filled rectangle. The specified height is taken as a
102  * minimum value, and it will be expanded vertically to fit the given text.
103  * @return The height of the rectangle
104  */
show_backgrounded_text_rectangle(const char * text,struct font * font,int x,int y,int w,int h)105 int show_backgrounded_text_rectangle(const char *text, struct font *font, int x, int y, int w, int h)
106 {
107 	struct font *old_font = get_current_font();
108 	set_current_font(font);
109 
110 	SDL_Rect t_rect;
111 	t_rect.x = x;
112 	t_rect.y = y;
113 
114 	// Find out the number of lines the text will occupy. This is done by
115 	// using the drawing function with drawing temporarily disabled.
116 	t_rect.w = w - (IN_WINDOW_TEXT_OFFSET * 2);
117 	t_rect.h = GameConfig.screen_height;
118 	int lines = get_lines_needed(text, t_rect, 1.0);
119 
120 	// Calculate the rectangle height
121 	int f_height = get_font_height(get_current_font());
122 	int r_height = (lines * f_height) + (IN_WINDOW_TEXT_OFFSET * 2);
123 
124 	// Set up and fill the rectangle.
125 	t_rect.w = w;
126 	t_rect.h = r_height;
127 	draw_rectangle(&t_rect, 0, 0, 0, 255);
128 
129 	// Show the text inside our newly drawn rectangle.
130 	t_rect.w -= IN_WINDOW_TEXT_OFFSET * 2;
131 	t_rect.h -= IN_WINDOW_TEXT_OFFSET;
132 	t_rect.x += IN_WINDOW_TEXT_OFFSET;
133 	t_rect.y += IN_WINDOW_TEXT_OFFSET;
134 	display_text(text, t_rect.x, t_rect.y, &t_rect, 1.0);
135 
136 	set_current_font(old_font);
137 	return r_height;
138 }
139 
140 /**
141  *
142  */
CutDownStringToMaximalSize(char * StringToCut,int LengthInPixels)143 int CutDownStringToMaximalSize(char *StringToCut, int LengthInPixels)
144 {
145 	int StringIndex = 0;
146 	int i;
147 
148 	if (text_width(get_current_font(), StringToCut) <= LengthInPixels)
149 		return FALSE;
150 
151 	StringIndex = limit_text_width(get_current_font(), StringToCut, LengthInPixels);
152 	if (StringIndex < 1)
153 		return FALSE;
154 
155 	for (i = 0; i < 3; i++) {
156 		if (StringToCut[StringIndex + i] != 0) {
157 			StringToCut[StringIndex + i] = '.';
158 		} else
159 			return TRUE;
160 	}
161 	StringToCut[StringIndex + 3] = 0;
162 
163 	return TRUE;
164 };				// void CutDownStringToMaximalSize ( char* StringToCut , int LengthInPixels )
165 
166 /* -----------------------------------------------------------------
167  *
168  * This function scrolls a given text down inside the User-window,
169  * defined by the global SDL_Rect User_Rect
170  *
171  * ----------------------------------------------------------------- */
ScrollText(char * Text,const char * background_name)172 int ScrollText(char *Text, const char *background_name)
173 {
174 	int Number_Of_Line_Feeds = 0;	// number of lines used for the text
175 	int StartInsertLine, InsertLine;
176 	int speed = +1;
177 	int maxspeed = 8;
178 	int done = 0;
179 	SDL_Event event;
180 
181 	Activate_Conservative_Frame_Computation();
182 
183 	SDL_Rect ScrollRect = { User_Rect.x + 10, User_Rect.y, User_Rect.w - 20, User_Rect.h };
184 	set_current_font(Para_Font);
185 	StartInsertLine = ScrollRect.y + ScrollRect.h;
186 	InsertLine = StartInsertLine;
187 
188 	while (!done) {
189 
190 		if (background_name)
191 			blit_background(background_name);
192 		Number_Of_Line_Feeds = display_text(Text, ScrollRect.x, InsertLine, &ScrollRect, 1.0);
193 		// We might add some buttons to be displayed here, so that, if you don't have
194 		// a mouse wheel and don't know about cursor keys, you can still click on these
195 		// buttons to control the scrolling speed of the text.
196 		//
197 		ShowGenericButtonFromList(SCROLL_TEXT_UP_BUTTON);
198 		ShowGenericButtonFromList(SCROLL_TEXT_DOWN_BUTTON);
199 		blit_mouse_cursor();
200 		our_SDL_flip_wrapper();
201 
202 		while (SDL_PollEvent(&event)) {
203 			if (event.type == SDL_MOUSEBUTTONDOWN) {
204 				//mange the event to make it look like an arrow key press
205 				event.type = SDL_KEYDOWN;
206 				switch (event.button.button) {
207 					case SDL_BUTTON_WHEELUP:
208 						event.key.keysym.sym = SDLK_UP;
209 						break;
210 					case SDL_BUTTON_WHEELDOWN:
211 						event.key.keysym.sym = SDLK_DOWN;
212 						break;
213 					case SDL_BUTTON_LEFT:
214 						if (MouseCursorIsOnButton(SCROLL_TEXT_UP_BUTTON, GetMousePos_x(), GetMousePos_y())) {
215 							event.key.keysym.sym = SDLK_UP;
216 						} else if (MouseCursorIsOnButton(SCROLL_TEXT_DOWN_BUTTON, GetMousePos_x(), GetMousePos_y())) {
217 							event.key.keysym.sym = SDLK_DOWN;
218 						} else {
219 							done = 1;
220 						}
221 						break;
222 					default:
223 						;
224 				}
225 			}
226 
227 			if (event.type == SDL_KEYDOWN) {
228 				switch (event.key.keysym.sym) {
229 					case SDLK_UP:
230 						speed--;
231 						if (speed < -maxspeed)
232 							speed = -maxspeed;
233 						break;
234 					case SDLK_DOWN:
235 						speed++;
236 						if (speed > maxspeed)
237 							speed = maxspeed;
238 						break;
239 					case SDLK_ESCAPE:
240 						done = 1;
241 						break;
242 					default:
243 						;
244 				}
245 			}
246 
247 		}
248 
249 		InsertLine -= speed;
250 
251 		// impose some limit on the amount to scroll away downwards and upwards
252 		//
253 		if (InsertLine > StartInsertLine && (speed < 0)) {
254 			InsertLine = StartInsertLine;
255 			speed = 0;
256 		}
257 		if (InsertLine + (Number_Of_Line_Feeds + 1) * get_font_height(get_current_font()) < ScrollRect.y
258 		    && (speed > 0)) {
259 			InsertLine = ScrollRect.y - (Number_Of_Line_Feeds + 1) * get_font_height(get_current_font());
260 			speed = 0;
261 		}
262 
263 		SDL_Delay(20);
264 	}
265 
266 	while (MouseLeftPressed()) ;	// so that we don't touch again immediately.
267 
268 	return OK;
269 }
270 
271 /**
272  * This function sets a new text, that will be displayed in huge font
273  * directly over the combat window for a fixed duration of time, where
274  * only the time in midst of combat and with no other windows opened
275  * is counted.
276  */
SetNewBigScreenMessage(const char * ScreenMessageText)277 void SetNewBigScreenMessage(const char *ScreenMessageText)
278 {
279 	int i = MAX_BIG_SCREEN_MESSAGES - 1;
280 
281 	/* Free the last message that's going to be overwritten */
282 	if (Me.BigScreenMessage[i]) {
283 		free(Me.BigScreenMessage[i]);
284 		Me.BigScreenMessage[i] = NULL;
285 	}
286 
287 	while (i > 0) {
288 		Me.BigScreenMessage[i] = Me.BigScreenMessage[i - 1];
289 		Me.BigScreenMessageDuration[i] = Me.BigScreenMessageDuration[i - 1];
290 		i--;
291 	}
292 
293 	Me.BigScreenMessage[0] = MyMalloc(strlen(ScreenMessageText) + 1);
294 	strcpy(Me.BigScreenMessage[0], ScreenMessageText);
295 	Me.BigScreenMessageDuration[0] = 0;
296 
297 };				// void SetNewBigScreenMessage( char* ScreenMessageText )
298 
299 /**
300  * This function displays the currently defined Bigscreenmessage on the
301  * screen.  It will be called by AssembleCombatWindow.
302  */
DisplayBigScreenMessage(void)303 void DisplayBigScreenMessage(void)
304 {
305 	int i;
306 	int y_pos = 30;
307 
308 	for (i = 0; i < GameConfig.number_of_big_screen_messages; i++) {
309 		if (!Me.BigScreenMessage[i])
310 			continue;
311 
312 		char *text = Me.BigScreenMessage[i];
313 
314 		if (Me.BigScreenMessageDuration[i] < GameConfig.delay_for_big_screen_messages) {
315 			SDL_SetClipRect(Screen, NULL);
316 
317 			set_current_font(Menu_Font);
318 			int x_pos = GameConfig.screen_width/2 - text_width(get_current_font(), text)/2;
319 			display_text(text, x_pos, y_pos, NULL /* clip */, 1.0);
320 
321 			if (!GameConfig.Inventory_Visible && !GameConfig.SkillScreen_Visible && !GameConfig.CharacterScreen_Visible)
322 				Me.BigScreenMessageDuration[i] += Frame_Time();
323 
324 			y_pos += get_font_height(Menu_Font);
325 		}
326 	}
327 
328 };				// void DisplayBigScreenMessage( void )
329 
display_countdown(int pixel_x,int pixel_y,const char * message,float duration)330 static void display_countdown(int pixel_x, int pixel_y, const char* message, float duration)
331 {
332 	char text[100];
333 	int minutes = ((int) duration) / 60;
334 	int seconds = ((int) duration) - (minutes * 60);
335 
336 	if (minutes >= 60) {
337 		sprintf(text, "%s", message);
338 	} else if (minutes) {
339 		sprintf(text, "%s %dm%ds", message, minutes, seconds);
340 	} else {
341 		sprintf(text, "%s %.1fs", message, duration);
342 	}
343 
344 	pixel_x -= text_width(get_current_font(), text) / 2;
345 
346 	display_text(text, pixel_x, pixel_y, NULL /* clip */, 1.0);
347 }
348 
349 /**
350  * This function displays the remaining seconds of effect on Tux above him.
351  */
display_effect_countdowns(void)352 void display_effect_countdowns(void)
353 {
354 	int pixel_x, pixel_y;
355 	translate_map_point_to_screen_pixel(Me.pos.x, Me.pos.y, &pixel_x, &pixel_y);
356 	pixel_y -= 128; /* tux height... */
357 
358 	if (Me.slowdown_duration > 0.0) {
359 		set_current_font(Blue_Font);
360 		display_countdown(pixel_x, pixel_y, _("slowed"), Me.slowdown_duration);
361 		pixel_y -= get_font_height(Blue_Font);
362 	}
363 
364 	if (Me.paralyze_duration > 0.0) {
365 		set_current_font(Red_Font);
366 		display_countdown(pixel_x, pixel_y, _("paralyzed"), Me.paralyze_duration);
367 		pixel_y -= get_font_height(Red_Font);
368 	}
369 
370 	if (Me.invisible_duration > 0.0) {
371 		set_current_font(FPS_Display_Font);
372 		display_countdown(pixel_x, pixel_y, _("invisible"), Me.invisible_duration);
373 		pixel_y -= get_font_height(FPS_Display_Font);
374 	}
375 
376 	if (Me.nmap_duration > 0.0) {
377 		set_current_font(FPS_Display_Font);
378 		display_countdown(pixel_x, pixel_y, _("scanning"), Me.nmap_duration);
379 	}
380 };		// void display_effect_countdowns( void )
381 
display_text_with_cursor(const char * text,int startx,int starty,const SDL_Rect * clip,float line_height_factor,int curpos)382 static int display_text_with_cursor(const char *text, int startx, int starty, const SDL_Rect *clip, float line_height_factor, int curpos)
383 {
384 	char *tmp;		// mobile pointer to the current position in the string to be printed
385 	SDL_Rect Temp_Clipping_Rect;	// adding this to prevent segfault in case of NULL as parameter
386 	SDL_Rect store_clip;
387 	short int nblines = 1;
388 	int empty_lines_started = 0;
389 	int current_curpos = 0;
390 
391 	if (text == NULL)
392 		return 0;
393 
394 	int letter_spacing = get_letter_spacing(get_current_font());
395 	int tab_width = TABWIDTH * (font_char_width(get_current_font(), TABCHAR) + letter_spacing);
396 
397 	if (text[0]=='\0')
398 		nblines = 0;
399 
400 	// We position the internal text cursor on the right spot for
401 	// the first character to be printed.
402 	//
403 	MyCursorX = startx;
404 	MyCursorY = starty;
405 
406 	int cursor_x = MyCursorX;
407 	int cursor_y = MyCursorY;
408 
409 	// We make a backup of the current clipping rect, so we can restore
410 	// it later.
411 	//
412 	SDL_GetClipRect(Screen, &store_clip);
413 
414 	// If we did receive some clipping rect in the parameter list (like e.g. it's
415 	// always the case with dialog output) we enforce this new clipping rect, otherwise
416 	// we just set the clipping rect to contain the whole screen.
417 	//
418 	if (clip != NULL) {
419 		SDL_SetClipRect(Screen, clip);
420 	} else {
421 		clip = &Temp_Clipping_Rect;
422 		Temp_Clipping_Rect.x = 0;
423 		Temp_Clipping_Rect.y = 0;
424 		Temp_Clipping_Rect.w = GameConfig.screen_width;
425 		Temp_Clipping_Rect.h = GameConfig.screen_height;
426 	}
427 
428 	set_gl_clip_rect(clip);
429 
430 	start_image_batch();
431 
432 	// Now we can start to print the actual text to the screen.
433 	//
434 	// The running text pointer must be initialized.
435 	//
436 	tmp = (char *)text;	// this is no longer a 'const' char*, but only a char*
437 	while (*tmp && (MyCursorY < clip->y + clip->h)) {
438 		if (current_curpos == curpos) {
439 			cursor_x = MyCursorX;
440 			cursor_y = MyCursorY;
441 		}
442 
443 		if (handle_switch_font_char(&tmp)) {
444 			current_curpos++;
445 			continue;
446 		}
447 
448 		if (((*tmp == ' ') || (*tmp == '\t'))
449 		    && (ImprovedCheckLineBreak(tmp, clip, line_height_factor) == 1))	// don't write over right border
450 		{		/*THE CALL ABOVE HAS DONE THE CARRIAGE RETURN FOR US !!! */
451 			empty_lines_started++;
452 			tmp++;
453 			current_curpos++;
454 			continue;
455 		}
456 
457 		// carriage return in the middle of a word if it is too big to fit on one line
458 		if (isgraph(*tmp) && (MyCursorX + font_char_width(get_current_font(), *tmp) + letter_spacing) > (clip->x + clip->w)) {
459 			MyCursorX = clip->x;
460 			MyCursorY += (int)(get_font_height(get_current_font()) * line_height_factor);
461 			empty_lines_started++;
462 		}
463 
464 		switch (*tmp) {
465 		case '\n':
466 			MyCursorX = clip->x;
467 			MyCursorY += (int)(get_font_height(get_current_font()) * line_height_factor);
468 			empty_lines_started++;
469 			break;
470 		case '\t':
471 			MyCursorX = (int)ceilf((float)MyCursorX / (float)(tab_width)) * (tab_width);
472 			break;
473 		default:
474 			if (MyCursorY > clip->y - (int)(get_font_height(get_current_font()) * line_height_factor)
475 				&& !display_char_disabled)
476 				put_char(get_current_font(), MyCursorX, MyCursorY, *tmp);
477 
478 			MyCursorX += font_char_width(get_current_font(), *tmp)
479 				+ get_letter_spacing(get_current_font());
480 
481 			// At least one visible character must follow a line break or else
482 			// the line is a trailing empty line not visible to the user. Such
483 			// empty lines don't contribute to the visual height of the string.
484 			if (isgraph(*tmp)) {
485 				nblines += empty_lines_started;
486 				empty_lines_started = 0;
487 			}
488 		}
489 		tmp++;
490 		current_curpos++;
491 	}
492 
493 	end_image_batch();
494 
495 	SDL_SetClipRect(Screen, &store_clip);	// restore previous clip-rect
496 
497 	if (curpos != -1) {
498 		if (curpos != 0 && current_curpos == curpos) {
499 			cursor_x = MyCursorX;
500 			cursor_y = MyCursorY;
501 		}
502 
503 		SDL_Rect cursor_rect;
504 		cursor_rect.x = cursor_x;
505 		cursor_rect.y = cursor_y;
506 		cursor_rect.h = get_font_height(get_current_font());
507 		cursor_rect.w = 8;
508 		HighlightRectangle(Screen, cursor_rect);
509 	}
510 
511 	if (use_open_gl)
512 		unset_gl_clip_rect();
513 
514 	return nblines;
515 }
516 
517 /**
518  * Prints text at given positions, automatically word-wrapping at the
519  * edges of clip_rect.  If clip_rect is NULL, no clipping is performed.
520  *
521  * @return number of lines written (from the first text line up to the
522  *         last displayed line)
523  */
display_text(const char * text,int startx,int starty,const SDL_Rect * clip,float line_height_factor)524 int display_text(const char *text, int startx, int starty, const SDL_Rect *clip, float line_height_factor)
525 {
526 	return display_text_with_cursor(text, startx, starty, clip, line_height_factor, -1);
527 }
528 
529 /**
530  * This function checks if the next word still fits in this line
531  * of text and initiates a carriage return/line feed if not.
532  * Very handy and convenient, for that means it is no longer necessary
533  * to enter \n in the text every time its time for a newline. cool.
534  *
535  * rp: added argument clip, which contains the text-window we're writing in
536  *     (formerly known as "TextBorder")
537  *
538  * ah: added return value : 1 if carriage return was done, FALSE otherwise
539  */
ImprovedCheckLineBreak(char * Resttext,const SDL_Rect * clip,float line_height_factor)540 int ImprovedCheckLineBreak(char *Resttext, const SDL_Rect * clip, float line_height_factor)
541 {
542 	int NeededSpace = 0;
543 
544 	int letter_spacing = get_letter_spacing(get_current_font());
545 
546 	if (*Resttext == ' ')
547 		NeededSpace = font_char_width(get_current_font(), ' ') + letter_spacing;
548 	else if (*Resttext == '\t')
549 		NeededSpace = TABWIDTH * (font_char_width(get_current_font(), TABCHAR) + letter_spacing);
550 
551 	Resttext++;
552 	while ((*Resttext != ' ') && (*Resttext != '\t') && (*Resttext != '\n') && (*Resttext != 0)) {
553 		NeededSpace += font_char_width(get_current_font(), *Resttext) + letter_spacing;
554 		Resttext++;
555 	}
556 
557 	if ((MyCursorX + NeededSpace) > (clip->x + clip->w)) {
558 		MyCursorX = clip->x;
559 		MyCursorY += (int)(get_font_height(get_current_font()) * line_height_factor);
560 		return 1;
561 	}
562 
563 	return 0;
564 };				// int ImprovedCheckLineBreak()
565 
566 /**
567  * Prompt the user for a string no longer than MaxLen (excluding terminating \0).
568  */
get_string(int MaxLen,const char * background_name,const char * text_for_overhead_promt)569 char *get_string(int MaxLen, const char *background_name, const char *text_for_overhead_promt)
570 {
571 	char *input;		// pointer to the string entered by the user
572 	int key;		// last 'character' entered
573 	int curpos;		// counts the characters entered so far
574 	int finished;
575 	int x0, y0;
576 
577 	display_text(text_for_overhead_promt, 50, 50, NULL, 1.0);
578 
579 	// allocate memory for the users input
580 	input = MyMalloc(MaxLen + 5);
581 
582 	memset(input, '.', MaxLen);
583 	input[MaxLen] = 0;
584 
585 	finished = FALSE;
586 	curpos = 0;
587 
588 	while (!finished) {
589 		blit_background(background_name);
590 		display_text(text_for_overhead_promt, 50, 50, NULL, 1.0);
591 
592 		x0 = MyCursorX;
593 		y0 = MyCursorY;
594 
595 		put_string(get_current_font(), x0, y0, input);
596 		our_SDL_flip_wrapper();
597 
598 		key = getchar_raw(NULL);
599 
600 		if (key == SDLK_RETURN) {
601 			// Display the image again so both buffers are in sync
602 			// useful for GL drivers that do true pageflipping (win32, nvidia 173.x, ...)
603 			if (use_open_gl) {
604 				blit_background(background_name);
605 				display_text(text_for_overhead_promt, 50, 50, NULL, 1.0);
606 				x0 = MyCursorX;
607 				y0 = MyCursorY;
608 				put_string(get_current_font(), x0, y0, input);
609 				our_SDL_flip_wrapper();
610 			}
611 			input[curpos] = 0;
612 			finished = TRUE;
613 		} else if ((key < SDLK_DELETE) && isprint(key) && (curpos < MaxLen)) {
614 			/* printable characters are entered in string */
615 			input[curpos] = (char)key;
616 			curpos++;
617 		} else if ((key <= SDLK_KP9) && (key >= SDLK_KP0) && (curpos < MaxLen)) {
618 			key -= SDLK_KP0;
619 			key += '0';
620 
621 			input[curpos] = (char)key;
622 			curpos++;
623 		} else if (key == SDLK_BACKSPACE) {
624 			if (curpos > 0)
625 				curpos--;
626 			input[curpos] = '.';
627 		} else if (key == SDLK_ESCAPE) {
628 			free(input);
629 			while (EscapePressed()) ;
630 			return (NULL);
631 		}
632 	}
633 
634 	return (input);
635 }
636 
637 /* -----------------------------------------------------------------
638  * This function reads a string of "MaxLen" from User-input.
639  *
640  * NOTE: MaxLen is the maximal _strlen_ of the string (excl. \0 !)
641  *
642  * ----------------------------------------------------------------- */
GetEditableStringInPopupWindow(int MaxLen,const char * PopupWindowTitle,const char * DefaultString)643 char *GetEditableStringInPopupWindow(int MaxLen, const char *PopupWindowTitle, const char *DefaultString)
644 {
645 	char *input;		// pointer to the string entered by the user
646 	int key;		// last 'character' entered
647 	int curpos;		// counts the characters entered so far
648 	int finished;
649 	int x0, y0;
650 	SDL_Rect TargetRect;
651 	int i;
652 
653 #define EDIT_WINDOW_TEXT_OFFSET 15
654 
655 	input = MyMalloc(MaxLen + 5);
656 	strncpy(input, DefaultString, MaxLen - 1);
657 	input[MaxLen] = 0;
658 	curpos = strlen(input);
659 
660 	// Now we prepare a rectangle for our popup window...
661 	//
662 	TargetRect.w = 440;
663 	TargetRect.h = 340;
664 	TargetRect.x = (640 - TargetRect.w) / 2;
665 	TargetRect.y = (480 - TargetRect.h) / 2;
666 	TargetRect.w -= EDIT_WINDOW_TEXT_OFFSET;
667 	TargetRect.h -= EDIT_WINDOW_TEXT_OFFSET;
668 	TargetRect.x += EDIT_WINDOW_TEXT_OFFSET;
669 	TargetRect.y += EDIT_WINDOW_TEXT_OFFSET;
670 
671 	// Now we find the right position for the new string to start by writing
672 	// out the title text once, just to get the cursor positioned right...
673 	//
674 	display_text(PopupWindowTitle, TargetRect.x, TargetRect.y, &TargetRect, 1.0);
675 	x0 = MyCursorX;
676 	y0 = MyCursorY;
677 
678 	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
679 	// Now we can start the enter-string cycle...
680 	//
681 	finished = FALSE;
682 	while (!finished) {
683 
684 		TargetRect.w = 440;
685 		TargetRect.h = 340;
686 		TargetRect.x = (640 - TargetRect.w) / 2;
687 		TargetRect.y = (480 - TargetRect.h) / 2;
688 		draw_rectangle(&TargetRect, 0, 0, 0, 255);
689 
690 		TargetRect.w -= EDIT_WINDOW_TEXT_OFFSET;
691 		TargetRect.h -= EDIT_WINDOW_TEXT_OFFSET;
692 		TargetRect.x += EDIT_WINDOW_TEXT_OFFSET;
693 		TargetRect.y += EDIT_WINDOW_TEXT_OFFSET;
694 
695 		set_current_font(FPS_Display_Font);
696 
697 		display_text(PopupWindowTitle, TargetRect.x, TargetRect.y, &TargetRect, 1.0);
698 
699 		if (PopupWindowTitle[strlen(PopupWindowTitle) - 1] != '\n')
700 			display_text("\n\n", x0, y0, &TargetRect, 1.0);
701 
702 		TargetRect.x = MyCursorX;
703 		TargetRect.y = MyCursorY;
704 		display_text_with_cursor(input, TargetRect.x, TargetRect.y, &TargetRect, 1.0, curpos);
705 
706 		our_SDL_flip_wrapper();
707 
708 		key = getchar_raw(NULL);
709 
710 		if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
711 			// input[curpos] = 0;
712 			finished = TRUE;
713 		} else if (key == SDLK_ESCAPE) {
714 			while (EscapePressed())
715 				SDL_Delay(1);
716 			free(input);
717 			return NULL;
718 		} else if (isprint(key) && (curpos < MaxLen)) {
719 			// If a printable character has been entered, it is either appended to
720 			// the end of the current input string or the rest of the string is being
721 			// moved and the new character inserted at the end.
722 			//
723 			if (curpos == ((int)strlen(input))) {
724 				input[curpos] = (char)key;
725 				curpos++;
726 				input[curpos] = 0;
727 			} else {
728 				if (((int)strlen(input)) == MaxLen - 1)
729 					input[MaxLen - 2] = 0;
730 				for (i = strlen(input); i >= curpos; i--) {
731 					input[i + 1] = input[i];
732 				}
733 				input[curpos] = (char)key;
734 				curpos++;
735 			}
736 		} else if (key == SDLK_LEFT) {
737 			if (curpos > 0)
738 				curpos--;
739 			// input[curpos] = '.';
740 		} else if (key == SDLK_RIGHT) {
741 			if (curpos < ((int)strlen(input)))
742 				curpos++;
743 			// input[curpos] = '.';
744 		} else if (key == SDLK_BACKSPACE) {
745 			if (curpos > 0) {
746 				i = curpos;
747 				while (input[i - 1] != 0) {
748 					input[i - 1] = input[i];
749 					i++;
750 				}
751 				curpos--;
752 			}
753 		} else if (key == SDLK_DELETE) {
754 			if (curpos > 0) {
755 				i = curpos;
756 				while (input[i] != 0) {
757 					input[i] = input[i + 1];
758 					i++;
759 				}
760 			}
761 
762 		} else if ((key <= SDLK_KP9) && (key >= SDLK_KP0) && (curpos < MaxLen)) {
763 			key -= SDLK_KP0;
764 			key += '0';
765 
766 			input[curpos] = (char)key;
767 			curpos++;
768 		}
769 
770 	}			// while ( ! finished )
771 
772 	SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
773 
774 	return input;
775 }
776 
777 /* -----------------------------------------------------------------
778  * behaves similarly as gl_printf() of svgalib, using the BFont
779  * print function PrintString().
780  *
781  *  sets current position of MyCursor[XY],
782  *     if last char is '\n': to same x, next line y
783  *     to end of string otherwise
784  *
785  * Added functionality to PrintString() is:
786  *  o) passing -1 as coord uses previous x and next-line y for printing
787  *  o) Screen is updated immediately after print, using SDL_flip()
788  *
789  * ----------------------------------------------------------------- */
printf_SDL(SDL_Surface * screen,int x,int y,const char * fmt,...)790 void printf_SDL(SDL_Surface * screen, int x, int y, const char *fmt, ...)
791 {
792 	va_list args;
793 	int i;
794 
795 	char *tmp;
796 	va_start(args, fmt);
797 
798 	if (x == -1)
799 		x = MyCursorX;
800 	else
801 		MyCursorX = x;
802 
803 	if (y == -1)
804 		y = MyCursorY;
805 	else
806 		MyCursorY = y;
807 
808 	tmp = (char *)MyMalloc(10000 + 1);
809 	vsprintf(tmp, fmt, args);
810 	put_string(get_current_font(), x, y, tmp);
811 
812 	// our_SDL_flip_wrapper (screen);
813 
814 	if (tmp[strlen(tmp) - 1] == '\n') {
815 		MyCursorX = x;
816 		MyCursorY = y + 1.1 * (get_font_height(get_current_font()));
817 	} else {
818 		for (i = 0; i < ((int)strlen(tmp)); i++)
819 			MyCursorX += font_char_width(get_current_font(), tmp[i]);
820 		MyCursorY = y;
821 	}
822 
823 	free(tmp);
824 	va_end(args);
825 
826 };				// void printf_SDL (SDL_Surface *screen, int x, int y, char *fmt, ...)
827 
828 /**
829  * Find the longest line in text, taking letter-spacing into account. Return the
830  * minimum line width needed to print that line, using the current font.
831  */
longest_line_width(char * text)832 int longest_line_width(char *text)
833 {
834 	int width = 0;
835 	char *line_start = text;
836 
837 	for (; TRUE; text++) {
838 		// Find the end of the current line.
839 		if (*text != '\n' && *text != '\0') {
840 			continue;
841 		}
842 
843 		// Get the width of the current line.
844 		char tmp = *text;
845 		*text = '\0';
846 		int line_width = text_width(get_current_font(), line_start);
847 		*text = tmp;
848 
849 		// Update the width of the longest line.
850 		if (line_width > width) {
851 			width = line_width;
852 		}
853 
854 		// Handle the next line, if any remain.
855 		if (*text != '\0') {
856 			line_start = text + 1;
857 		} else {
858 			break;
859 		}
860 	}
861 
862 	return width;
863 }
864 
865 #undef _text_c
866