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