1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /** @file
21  *  Functions for the edit box widget.
22  */
23 
24 #include <string.h>
25 
26 #include "lib/framework/frame.h"
27 #include "lib/framework/utf.h"
28 #include "lib/framework/wzapp.h"
29 #include "widget.h"
30 #include "widgint.h"
31 #include "editbox.h"
32 #include "form.h"
33 #include "lib/ivis_opengl/pieblitfunc.h"
34 
35 
36 /* Pixel gap between edge of edit box and text */
37 #define WEDB_XGAP	4
38 
39 /* Size of the overwrite cursor */
40 #define WEDB_CURSORSIZE		8
41 
42 /* Whether the cursor blinks or not */
43 #define CURSOR_BLINK		1
44 
45 /* The time the cursor blinks for */
46 #define WEDB_BLINKRATE		800
47 
48 /* Number of characters to jump the edit box text when moving the cursor */
49 #define WEDB_CHARJUMP		6
50 
51 // Max size for a string in a editbox
52 #define EB_MAX_STRINGSIZE 72
53 
W_EDBINIT()54 W_EDBINIT::W_EDBINIT()
55 	: pText(nullptr)
56 	, FontID(font_regular)
57 	, pBoxDisplay(nullptr)
58 {}
59 
W_EDITBOX(W_EDBINIT const * init)60 W_EDITBOX::W_EDITBOX(W_EDBINIT const *init)
61 	: WIDGET(init, WIDG_EDITBOX)
62 	, state(WEDBS_FIXED)
63 	, FontID(init->FontID)
64 	, blinkOffset(wzGetTicks())
65 	, maxStringSize(EB_MAX_STRINGSIZE)
66 	, insPos(0)
67 	, printStart(0)
68 	, printChars(0)
69 	, printWidth(0)
70 	, pBoxDisplay(init->pBoxDisplay)
71 	, HilightAudioID(WidgGetHilightAudioID())
72 	, ClickedAudioID(WidgGetClickedAudioID())
73 	, ErrorAudioID(WidgGetErrorAudioID())
74 	, AudioCallback(WidgGetAudioCallback())
75 	, boxColourFirst(WZCOL_FORM_DARK)
76 	, boxColourSecond(WZCOL_FORM_LIGHT)
77 	, boxColourBackground(WZCOL_FORM_BACKGROUND)
78 {
79 	char const *text = init->pText;
80 	if (!text)
81 	{
82 		text = "";
83 	}
84 	aText = WzString::fromUtf8(text);
85 
86 	initialise();
87 
88 	ASSERT((init->style & ~(WEDB_PLAIN | WIDG_HIDDEN)) == 0, "Unknown edit box style");
89 }
90 
W_EDITBOX()91 W_EDITBOX::W_EDITBOX()
92 	: WIDGET()
93 	, state(WEDBS_FIXED)
94 	, FontID(font_regular)
95 	, blinkOffset(wzGetTicks())
96 	, maxStringSize(EB_MAX_STRINGSIZE)
97 	, insPos(0)
98 	, printStart(0)
99 	, printChars(0)
100 	, printWidth(0)
101 	, pBoxDisplay(nullptr)
102 	, HilightAudioID(WidgGetHilightAudioID())
103 	, ClickedAudioID(WidgGetClickedAudioID())
104 	, ErrorAudioID(WidgGetErrorAudioID())
105 	, AudioCallback(WidgGetAudioCallback())
106 	, boxColourFirst(WZCOL_FORM_DARK)
107 	, boxColourSecond(WZCOL_FORM_LIGHT)
108 	, boxColourBackground(WZCOL_FORM_BACKGROUND)
109 {}
110 
~W_EDITBOX()111 W_EDITBOX::~W_EDITBOX()
112 {
113 	/* Note the edit state */
114 	unsigned editState = state & WEDBS_MASK;
115 
116 	/* Only have anything to do if the widget is being edited */
117 	if ((editState & WEDBS_MASK) == WEDBS_FIXED)
118 	{
119 		return;
120 	}
121 
122 	// If the edit box still somehow has focus, and is editable, need to StopTextInput()
123 	// (May be able to remove this once more refactoring of the game menus / in-game UI occurs)
124 	debug(LOG_INFO, "Editbox seems to still have focus, and is editable, as it's being destroyed.");
125 	StopTextInput(this); // force-stop text input if this EditBox somehow still has the input
126 }
127 
initialise()128 void W_EDITBOX::initialise()
129 {
130 	state = WEDBS_FIXED;
131 	printStart = 0;
132 	maxStringSize = EB_MAX_STRINGSIZE;
133 	fitStringStart();
134 }
135 
136 
137 /* Insert a character into a text buffer */
insertChar(WzUniCodepoint ch)138 bool W_EDITBOX::insertChar(WzUniCodepoint ch)
139 {
140 	if (ch.isNull())
141 	{
142 		return false;
143 	}
144 
145 	ASSERT(insPos <= aText.length(), "Invalid insertion point");
146 	if (aText.length() >= maxStringSize)
147 	{
148 		if (AudioCallback)
149 		{
150 			AudioCallback(ErrorAudioID);
151 		}
152 		return false;		// string too big, just return
153 	}
154 	/* Move the end of the string up by one (including terminating \0) */
155 	/* Insert the character */
156 	aText.insert(insPos, ch);
157 
158 	/* Update the insertion point */
159 	++insPos;
160 
161 	return true;
162 }
163 
164 
165 /* Put a character into a text buffer overwriting any text under the cursor */
overwriteChar(WzUniCodepoint ch)166 bool W_EDITBOX::overwriteChar(WzUniCodepoint ch)
167 {
168 	if (ch.isNull())
169 	{
170 		return false;
171 	}
172 
173 	ASSERT(insPos <= aText.length(), "overwriteChar: Invalid insertion point");
174 	dirty = true;
175 
176 	if (insPos == aText.length())
177 	{
178 		// At end of string.
179 		return insertChar(ch);
180 	}
181 
182 	/* Store the character */
183 	aText[insPos] = ch;
184 
185 	/* Update the insertion point */
186 	++insPos;
187 
188 	return true;
189 }
190 
191 
192 /* Delete a character to the right of the position */
delCharRight()193 void W_EDITBOX::delCharRight()
194 {
195 	ASSERT(insPos <= aText.length(), "Invalid deletion point");
196 
197 	/* Can't delete if we are at the end of the string */
198 	/* Move the end of the string down by one */
199 	aText.remove(insPos, 1);
200 }
201 
202 
203 /* Delete a character to the left of the position */
delCharLeft()204 void W_EDITBOX::delCharLeft()
205 {
206 	/* Can't delete if we are at the start of the string */
207 	if (insPos == 0)
208 	{
209 		return;
210 	}
211 
212 	--insPos;
213 	delCharRight();
214 }
215 
216 
geometryChanged()217 void W_EDITBOX::geometryChanged()
218 {
219 	/* Note the edit state */
220 	unsigned editState = state & WEDBS_MASK;
221 
222 	/* For now, only handle fit recalculation if not being edited */
223 	if (!((editState & WEDBS_MASK) == WEDBS_FIXED))
224 	{
225 		return;
226 	}
227 	fitStringStart();
228 }
229 
230 
231 /* Calculate how much of the start of a string can fit into the edit box */
fitStringStart()232 void W_EDITBOX::fitStringStart()
233 {
234 	// We need to calculate the whole string's pixel size.
235 	// From QuesoGLC's notes: additional processing like kerning creates strings of text whose dimensions are not directly
236 	// related to the simple juxtaposition of individual glyph metrics. For example, the advance width of "VA" isn't the
237 	// sum of the advances of "V" and "A" taken separately.
238 	WzString tmp = aText;
239 	tmp.remove(0, printStart);  // Ignore the first printStart characters.
240 
241 	while (!tmp.isEmpty())
242 	{
243 		int pixelWidth = iV_GetTextWidth(tmp.toUtf8().c_str(), FontID);
244 
245 		if (pixelWidth <= width() - (WEDB_XGAP * 2 + WEDB_CURSORSIZE))
246 		{
247 			printChars = tmp.length();
248 			printWidth = pixelWidth;
249 			return;
250 		}
251 
252 		tmp.remove(tmp.length() - 1, 1);  // Erase last char.
253 	}
254 
255 	printChars = 0;
256 	printWidth = 0;
257 }
258 
259 
260 /* Calculate how much of the end of a string can fit into the edit box */
fitStringEnd()261 void W_EDITBOX::fitStringEnd()
262 {
263 	WzString tmp = aText;
264 
265 	printStart = 0;
266 
267 	while (!tmp.isEmpty())
268 	{
269 		int pixelWidth = iV_GetTextWidth(tmp.toUtf8().c_str(), FontID);
270 
271 		if (pixelWidth <= width() - (WEDB_XGAP * 2 + WEDB_CURSORSIZE))
272 		{
273 			printChars = tmp.length();
274 			printWidth = pixelWidth;
275 			return;
276 		}
277 
278 		tmp.remove(0, 1);  // Erase first char.
279 		++printStart;
280 	}
281 
282 	printChars = 0;
283 	printWidth = 0;
284 }
285 
setCursorPosPixels(int xPos)286 void W_EDITBOX::setCursorPosPixels(int xPos)
287 {
288 	WzString tmp = aText;
289 	tmp.remove(0, printStart);  // Consider only the visible text.
290 	tmp.remove(printChars, tmp.length());
291 
292 	int prevDelta = INT32_MAX;
293 	int prevPos = printStart + tmp.length();
294 	while (!tmp.isEmpty())
295 	{
296 		int pixelWidth = iV_GetTextWidth(tmp.toUtf8().c_str(), FontID);
297 		int delta = pixelWidth - (xPos - (WEDB_XGAP + WEDB_CURSORSIZE / 2));
298 		int pos = printStart + tmp.length();
299 
300 		if (delta <= 0)
301 		{
302 			insPos = -delta < prevDelta ? pos : prevPos;
303 			return;
304 		}
305 
306 		tmp.remove(tmp.length() - 1, 1);  // Erase last char.
307 
308 		prevDelta = delta;
309 		prevPos = pos;
310 	}
311 
312 	insPos = printStart;
313 }
314 
315 
run(W_CONTEXT * psContext)316 void W_EDITBOX::run(W_CONTEXT *psContext)
317 {
318 	/* Note the edit state */
319 	unsigned editState = state & WEDBS_MASK;
320 
321 	/* Only have anything to do if the widget is being edited */
322 	if ((editState & WEDBS_MASK) == WEDBS_FIXED)
323 	{
324 		return;
325 	}
326 	dirty = true;
327 	StartTextInput(this);
328 	/* If there is a mouse click outside of the edit box - stop editing */
329 	int mx = psContext->mx;
330 	int my = psContext->my;
331 	if (mousePressed(MOUSE_LMB) && !geometry().contains(mx, my))
332 	{
333 		StopTextInput(this);
334 		if (auto lockedScreen = screenPointer.lock())
335 		{
336 			lockedScreen->setFocus(nullptr);
337 		}
338 		return;
339 	}
340 
341 	/* Loop through the characters in the input buffer */
342 	bool done = false;
343 	utf_32_char unicode;
344 	for (unsigned key = inputGetKey(&unicode); key != 0 && !done; key = inputGetKey(&unicode))
345 	{
346 		// Don't blink while typing.
347 		blinkOffset = wzGetTicks();
348 
349 		int len = 0;
350 
351 		/* Deal with all the control keys, assume anything else is a printable character */
352 		switch (key)
353 		{
354 		case INPBUF_LEFT :
355 			/* Move the cursor left */
356 			insPos = MAX(insPos - 1, 0);
357 
358 			/* If the cursor has gone off the left of the edit box,
359 			 * need to update the printable text.
360 			 */
361 			if (insPos < printStart)
362 			{
363 				printStart = MAX(printStart - WEDB_CHARJUMP, 0);
364 				fitStringStart();
365 			}
366 			debug(LOG_INPUT, "EditBox cursor left");
367 			break;
368 		case INPBUF_RIGHT :
369 			/* Move the cursor right */
370 			len = aText.length();
371 			insPos = MIN(insPos + 1, len);
372 
373 			/* If the cursor has gone off the right of the edit box,
374 			 * need to update the printable text.
375 			 */
376 			if (insPos > printStart + printChars)
377 			{
378 				printStart = MIN(printStart + WEDB_CHARJUMP, len - 1);
379 				fitStringStart();
380 			}
381 			debug(LOG_INPUT, "EditBox cursor right (%d, %d, %d)", insPos, printStart, printChars);
382 			break;
383 		case INPBUF_UP :
384 			debug(LOG_INPUT, "EditBox cursor up");
385 			break;
386 		case INPBUF_DOWN :
387 			debug(LOG_INPUT, "EditBox cursor down");
388 			break;
389 		case INPBUF_HOME :
390 			/* Move the cursor to the start of the buffer */
391 			insPos = 0;
392 			printStart = 0;
393 			fitStringStart();
394 			debug(LOG_INPUT, "EditBox cursor home");
395 			break;
396 		case INPBUF_END :
397 			/* Move the cursor to the end of the buffer */
398 			insPos = aText.length();
399 			if (insPos != printStart + printChars)
400 			{
401 				fitStringEnd();
402 			}
403 			debug(LOG_INPUT, "EditBox cursor end");
404 			break;
405 		case INPBUF_INS :
406 			if (editState == WEDBS_INSERT)
407 			{
408 				editState = WEDBS_OVER;
409 			}
410 			else
411 			{
412 				editState = WEDBS_INSERT;
413 			}
414 			debug(LOG_INPUT, "EditBox cursor insert");
415 			break;
416 		case INPBUF_DEL :
417 			delCharRight();
418 
419 			/* Update the printable text */
420 			fitStringStart();
421 			debug(LOG_INPUT, "EditBox cursor delete");
422 			break;
423 		case INPBUF_PGUP :
424 			debug(LOG_INPUT, "EditBox cursor page up");
425 			break;
426 		case INPBUF_PGDN :
427 			debug(LOG_INPUT, "EditBox cursor page down");
428 			break;
429 		case INPBUF_BKSPACE :
430 			/* Delete the character to the left of the cursor */
431 			delCharLeft();
432 
433 			/* Update the printable text */
434 			if (insPos <= printStart)
435 			{
436 				printStart = MAX(printStart - WEDB_CHARJUMP, 0);
437 			}
438 			fitStringStart();
439 			debug(LOG_INPUT, "EditBox cursor backspace");
440 			break;
441 		case INPBUF_TAB :
442 			debug(LOG_INPUT, "EditBox cursor tab");
443 			break;
444 		case INPBUF_CR :
445 		case KEY_KPENTER:					// either normal return key || keypad enter
446 			/* Finish editing */
447 			StopTextInput(this);
448 			if (auto lockedScreen = screenPointer.lock())
449 			{
450 				lockedScreen->setFocus(nullptr);
451 			}
452 			debug(LOG_INPUT, "EditBox cursor return");
453 			return;
454 			break;
455 		case INPBUF_ESC :
456 			debug(LOG_INPUT, "EditBox cursor escape");
457 			if (aText.length() > 0)
458 			{
459 				// hitting ESC while the editbox contains text clears the text
460 				aText.clear();
461 				insPos = 0;
462 				printStart = 0;
463 				fitStringStart();
464 				inputLoseFocus();	// clear the input buffer.
465 			}
466 			else
467 			{
468 				// hitting ESC while the editbox is empty ends editing mode
469 				StopTextInput(this);
470 				if (auto lockedScreen = screenPointer.lock())
471 				{
472 					lockedScreen->setFocus(nullptr);
473 				}
474 				inputLoseFocus();	// clear the input buffer.
475 				return;
476 			}
477 			break;
478 
479 		default:
480 			if (keyDown(KEY_LCTRL) || keyDown(KEY_RCTRL))
481 			{
482 				switch (key)
483 				{
484 				case KEY_V:
485 					aText = wzGetSelection();
486 					if (aText.length() >= maxStringSize)
487 					{
488 						aText.truncate(maxStringSize);
489 					}
490 					insPos = aText.length();
491 					/* Update the printable text */
492 					fitStringEnd();
493 					debug(LOG_INPUT, "EditBox paste");
494 					break;
495 				default:
496 					break;
497 				}
498 				break;
499 			}
500 			/* Dealt with everything else this must be a printable character */
501 			bool changedText = false;
502 			if (editState == WEDBS_INSERT)
503 			{
504 				changedText = insertChar(WzUniCodepoint::fromUTF32(unicode));
505 			}
506 			else
507 			{
508 				changedText = overwriteChar(WzUniCodepoint::fromUTF32(unicode));
509 			}
510 			if (changedText)
511 			{
512 				len = aText.length();
513 				/* Update the printable chars */
514 				if (insPos == len)
515 				{
516 					fitStringEnd();
517 				}
518 				else
519 				{
520 					fitStringStart();
521 					if (insPos > printStart + printChars)
522 					{
523 						printStart = MIN(printStart + WEDB_CHARJUMP, len - 1);
524 						if (printStart >= len)
525 						{
526 							fitStringStart();
527 						}
528 					}
529 				}
530 			}
531 			break;
532 		}
533 	}
534 
535 	/* Store the current widget state */
536 	state = (state & ~WEDBS_MASK) | editState;
537 }
538 
getString() const539 WzString W_EDITBOX::getString() const
540 {
541 	return aText;
542 }
543 
544 /* Set the current string for the edit box */
setString(WzString string)545 void W_EDITBOX::setString(WzString string)
546 {
547 	aText = string;
548 	initialise();
549 	dirty = true;
550 }
551 
simulateClick(W_CONTEXT * psContext,bool silenceClickAudio,WIDGET_KEY key)552 void W_EDITBOX::simulateClick(W_CONTEXT *psContext, bool silenceClickAudio /*= false*/, WIDGET_KEY key /*= WKEY_PRIMARY*/)
553 {
554 	if (silenceClickAudio)
555 	{
556 		suppressAudioCallback = true;
557 	}
558 	clicked(psContext, key);
559 	if (silenceClickAudio)
560 	{
561 		suppressAudioCallback = false;
562 	}
563 }
564 
565 /* Respond to a mouse click */
clicked(W_CONTEXT * psContext,WIDGET_KEY)566 void W_EDITBOX::clicked(W_CONTEXT *psContext, WIDGET_KEY)
567 {
568 	if (state & WEDBS_DISABLE)  // disabled button.
569 	{
570 		return;
571 	}
572 
573 	// Set cursor position to the click location.
574 	setCursorPosPixels(psContext->mx - x());
575 
576 	// Cursor should be visible instantly.
577 	blinkOffset = wzGetTicks();
578 
579 	if ((state & WEDBS_MASK) == WEDBS_FIXED)
580 	{
581 		if (AudioCallback && !suppressAudioCallback)
582 		{
583 			AudioCallback(ClickedAudioID);
584 		}
585 
586 		/* Set up the widget state */
587 		state = (state & ~WEDBS_MASK) | WEDBS_INSERT;
588 
589 		/* Calculate how much of the string can appear in the box */
590 		fitStringEnd();
591 
592 		/* Clear the input buffer */
593 		inputClearBuffer();
594 
595 		/* Tell the form that the edit box has focus */
596 		if (auto lockedScreen = screenPointer.lock())
597 		{
598 			lockedScreen->setFocus(shared_from_this());
599 		}
600 	}
601 	dirty = true;
602 }
603 
604 
605 /* Respond to loss of focus */
focusLost()606 void W_EDITBOX::focusLost()
607 {
608 	ASSERT(!(state & WEDBS_DISABLE), "editBoxFocusLost: disabled edit box");
609 
610 	/* Stop editing the widget */
611 	state = WEDBS_FIXED;
612 	printStart = 0;
613 	fitStringStart();
614 	StopTextInput(this);
615 
616 	if (auto lockedScreen = screenPointer.lock())
617 	{
618 		lockedScreen->setReturn(shared_from_this());
619 	}
620 	dirty = true;
621 }
622 
623 
624 /* Respond to a mouse moving over an edit box */
highlight(W_CONTEXT *)625 void W_EDITBOX::highlight(W_CONTEXT *)
626 {
627 	W_EDITBOX *psWidget = this;
628 	if (psWidget->state & WEDBS_DISABLE)
629 	{
630 		return;
631 	}
632 
633 	if (psWidget->AudioCallback)
634 	{
635 		psWidget->AudioCallback(psWidget->HilightAudioID);
636 	}
637 
638 	psWidget->state |= WEDBS_HILITE;
639 }
640 
641 
642 /* Respond to the mouse moving off an edit box */
highlightLost()643 void W_EDITBOX::highlightLost()
644 {
645 	W_EDITBOX *psWidget = this;
646 	if (psWidget->state & WEDBS_DISABLE)
647 	{
648 		return;
649 	}
650 
651 	psWidget->state = psWidget->state & WEDBS_MASK;
652 }
653 
setBoxColours(PIELIGHT first,PIELIGHT second,PIELIGHT background)654 void W_EDITBOX::setBoxColours(PIELIGHT first, PIELIGHT second, PIELIGHT background)
655 {
656 	boxColourFirst = first;
657 	boxColourSecond = second;
658 	boxColourBackground = background;
659 }
660 
display(int xOffset,int yOffset)661 void W_EDITBOX::display(int xOffset, int yOffset)
662 {
663 	int x0 = x() + xOffset;
664 	int y0 = y() + yOffset;
665 	int x1 = x0 + width();
666 	int y1 = y0 + height();
667 
668 	if (pBoxDisplay != nullptr)
669 	{
670 		pBoxDisplay(this, xOffset, yOffset);
671 	}
672 	else
673 	{
674 		iV_ShadowBox(x0, y0, x1, y1, 0, boxColourFirst, boxColourSecond, boxColourBackground);
675 	}
676 
677 	int fx = x0 + WEDB_XGAP;// + (psEdBox->width - fw) / 2;
678 
679 	int fy = y0 + (height() - iV_GetTextLineSize(FontID)) / 2 - iV_GetTextAboveBase(FontID);
680 
681 	/* If there is more text than will fit into the box, display the bit with the cursor in it */
682 	WzString displayedText = aText;
683 	displayedText.remove(0, printStart);  // Erase anything there isn't room to display.
684 	displayedText.remove(printChars, displayedText.length());
685 
686 	displayCache.wzDisplayedText.setText(displayedText.toUtf8(), FontID);
687 	displayCache.wzDisplayedText.render(fx, fy, WZCOL_FORM_TEXT);
688 
689 	// Display the cursor if editing
690 #if CURSOR_BLINK
691 	bool blink = !(((wzGetTicks() - blinkOffset) / WEDB_BLINKRATE) % 2);
692 	if ((state & WEDBS_MASK) == WEDBS_INSERT && blink)
693 #else
694 	if ((state & WEDBS_MASK) == WEDBS_INSERT)
695 #endif
696 	{
697 		// insert mode
698 		WzString tmp = aText;
699 		tmp.remove(insPos, tmp.length());         // Erase from the cursor on, to find where the cursor should be.
700 		tmp.remove(0, printStart);
701 
702 		displayCache.modeText.setText(tmp.toUtf8(), FontID);
703 
704 		int cx = x0 + WEDB_XGAP + displayCache.modeText.width();
705 		int cy = fy;
706 		iV_Line(cx, cy + iV_GetTextAboveBase(FontID), cx, cy - iV_GetTextBelowBase(FontID), WZCOL_FORM_CURSOR);
707 	}
708 #if CURSOR_BLINK
709 	else if ((state & WEDBS_MASK) == WEDBS_OVER && blink)
710 #else
711 	else if ((state & WEDBS_MASK) == WEDBS_OVER)
712 #endif
713 	{
714 		// overwrite mode
715 		WzString tmp = aText;
716 		tmp.remove(insPos, tmp.length());         // Erase from the cursor on, to find where the cursor should be.
717 		tmp.remove(0, printStart);
718 
719 		displayCache.modeText.setText(tmp.toUtf8(), FontID);
720 
721 		int cx = x0 + WEDB_XGAP + displayCache.modeText.width();
722 		int cy = fy;
723 		iV_Line(cx, cy, cx + WEDB_CURSORSIZE, cy, WZCOL_FORM_CURSOR);
724 	}
725 
726 	if (pBoxDisplay == nullptr)
727 	{
728 		if ((state & WEDBS_HILITE) != 0)
729 		{
730 			/* Display the button hilite */
731 			iV_Box(x0 - 2, y0 - 2, x1 + 2, y1 + 2, WZCOL_FORM_HILITE);
732 		}
733 	}
734 }
735 
setMaxStringSize(int size)736 void W_EDITBOX::setMaxStringSize(int size)
737 {
738 	maxStringSize = size;
739 }
740 
setState(unsigned newState)741 void W_EDITBOX::setState(unsigned newState)
742 {
743 	unsigned mask = WEDBS_DISABLE;
744 	state = (state & ~mask) | (newState & mask);
745 }
746