Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / gui / group_editbox.cpp
blob1410e2cb1b7c2067ef26cd495238e0711a4796b5
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013-2014 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "nel/gui/group_editbox.h"
24 #include "nel/misc/command.h"
25 #include "nel/misc/utf_string_view.h"
26 #include "nel/gui/view_text.h"
27 #include "nel/misc/xml_auto_ptr.h"
28 #include "nel/gui/interface_options.h"
29 #include "nel/gui/ctrl_draggable.h"
30 #include "nel/gui/group_container_base.h"
31 #include "nel/gui/lua_ihm.h"
32 #include "nel/gui/widget_manager.h"
33 #include "nel/gui/view_renderer.h"
34 #include "nel/gui/db_manager.h"
35 #include "nel/gui/interface_factory.h"
37 using namespace std;
38 using namespace NLMISC;
39 using namespace NL3D;
41 #ifdef DEBUG_NEW
42 #define new DEBUG_NEW
43 #endif
45 namespace NLGUI
49 /////////////
50 // STATICS //
51 /////////////
53 sint32 CGroupEditBox::_SelectCursorPos = 0;
54 CGroupEditBox *CGroupEditBox::_MenuFather = NULL;
55 CGroupEditBox::IComboKeyHandler* CGroupEditBox::comboKeyHandler = NULL;
57 // For now, just trim unsupported codepoints to make emoji fallback to text form
58 static u32char supportedCodepoint(u32char c)
60 if (c >= 0xFE00 && c < 0xFE10)
61 return 0; // Variation Selectors, unsupported
62 else if (c >= 0xE0100 && c < 0xE01F0)
63 return 0; // Variation Selectors Supplement, unsupported
64 else if (c >= 0x200B && c < 0x2010)
65 return 0; // ZERO WIDTH JOINER, etcetera, unsupported
66 else if (c >= 0x2028 && c < 0x202F)
67 return 0; // PARAGRAPH SEPARATOR, etcetera, unsupported
68 else if (c >= 0x2060 && c < 0x2070)
69 return 0; // WORD JOINER, etcetera, unsupported
70 else if (c == 0xFEFF)
71 return 0; // BOM, unsupported
72 else if ((c & 0xFC00) == 0xD800)
73 return 0xFFFD; // UTF-16 surrogate, unmatched pair, invalid, set to replacement character
74 else if ((c & 0xFC00) == 0xDC00)
75 return 0xFFFD; // UTF-16 surrogate, unmatched pair, invalid, set to replacement character
76 return c;
79 // For now, just trim unsupported codepoints to make emoji fallback to text form
80 static ::u32string trimUnsupported(const ::u32string str)
82 ::u32string res;
83 res.reserve(str.size());
84 for (::u32string::const_iterator it(str.begin()), end(str.end()); it != end; ++it)
86 u32char c = supportedCodepoint(*it);
87 if (c) // This also trims NUL
88 res.push_back(c);
90 return res;
93 // ----------------------------------------------------------------------------
94 NLMISC_REGISTER_OBJECT(CViewBase, CGroupEditBox, std::string, "edit_box");
96 CGroupEditBox::CGroupEditBox(const TCtorParam &param) :
97 CGroupEditBoxBase(param),
98 _BlinkTime(0.f),
99 _CursorPos(0),
100 _MaxNumChar(std::numeric_limits<uint32>::max()),
101 _MaxNumBytes(0),
102 _MaxNumReturn(std::numeric_limits<uint32>::max()),
103 _MaxFloatPrec(20),
104 _MaxCharsSize(32768),
105 _FirstVisibleChar(0),
106 _LastVisibleChar(0),
107 _SelectingText(false),
108 _ViewText(NULL),
109 _MaxHistoric(0),
110 _CurrentHistoricIndex(-1),
111 _PrevNumLine(1),
112 _EntryType(Text),
113 _Setupped(false),
114 _BypassNextKey(false),
115 _BlinkState(false),
116 _CursorAtPreviousLineEnd(false),
117 _LooseFocusOnEnter(true),
118 _ResetFocusOnHide(false),
119 _BackupFatherContainerPos(false),
120 _WantReturn(false),
121 _ClearOnEscape(false),
122 _Savable(true),
123 _DefaultInputString(false),
124 _Frozen(false),
125 _CanRedo(false),
126 _CanUndo(false),
127 _CursorTexID(-1),
128 _CursorWidth(0),
129 _IntegerMinValue(INT_MIN),
130 _IntegerMaxValue(INT_MAX),
131 _PositiveIntegerMinValue(0),
132 _PositiveIntegerMaxValue(UINT_MAX),
133 _ViewTextDeltaX(0)
136 _Prompt = ::u32string(1, (u32char)'>');
137 _BackSelectColor= CRGBA::White;
138 _TextSelectColor= CRGBA::Black;
141 // ----------------------------------------------------------------------------
142 CGroupEditBox::~CGroupEditBox()
144 if (this == _CurrSelection) _CurrSelection = NULL;
145 if (CWidgetManager::getInstance()->getCaptureKeyboard() == this || CWidgetManager::getInstance()->getOldCaptureKeyboard() == this)
147 CWidgetManager::getInstance()->resetCaptureKeyboard();
151 std::string CGroupEditBox::getProperty( const std::string &name ) const
153 if( name == "onchange" )
155 return _AHOnChange;
157 else
158 if( name == "onchange_params" )
160 return _ParamsOnChange;
162 else
163 if( name == "on_focus_lost" )
165 return _AHOnFocusLost;
167 else
168 if( name == "on_focus_lost_params" )
170 return _AHOnFocusLostParams;
172 else
173 if( name == "on_focus" )
175 return _AHOnFocus;
177 else
178 if( name == "on_focus_params" )
180 return _AHOnFocusParams;
182 else
183 if( name == "max_num_chars" )
185 return toString( _MaxNumChar );
187 else
188 if( name == "max_num_bytes" )
190 return toString( _MaxNumBytes );
192 else
193 if( name == "max_num_return" )
195 return toString( _MaxNumReturn );
197 else
198 if( name == "max_chars_size" )
200 return toString( _MaxCharsSize );
202 else
203 if( name == "enter_loose_focus" )
205 return toString( _LooseFocusOnEnter );
207 else
208 if( name == "enter_recover_focus" )
210 return toString( _RecoverFocusOnEnter );
212 else
213 if( name == "prompt" )
215 return CUtfStringView(_Prompt).toUtf8();
217 else
218 if( name == "enter_type" )
220 switch( _EntryType )
222 case Integer:
223 return "integer";
224 break;
226 case PositiveInteger:
227 return "positive_integer";
228 break;
230 case Float:
231 return "float";
232 break;
234 case PositiveFloat:
235 return "positive_float";
236 break;
238 case Alpha:
239 return "alpha";
240 break;
242 case AlphaNum:
243 return "alpha_num";
244 break;
246 case AlphaNumSpace:
247 return "alpha_num_space";
248 break;
250 case Password:
251 return "password";
252 break;
254 case Filename:
255 return "filename";
256 break;
258 case PlayerName:
259 return "playername";
260 break;
262 default:
263 break;
266 return "text";
268 else
269 if( name == "menu_r" )
271 return _ListMenuRight;
273 else
274 if( name == "max_historic" )
276 return toString( _MaxHistoric );
278 else
279 if( name == "backup_father_container_pos" )
281 return toString( _BackupFatherContainerPos );
283 else
284 if( name == "want_return" )
286 return toString( _WantReturn );
288 else
289 if( name == "clear_on_escape" )
291 return toString( _ClearOnEscape );
293 else
294 if( name == "savable" )
296 return toString( _Savable );
298 else
299 if( name == "max_float_prec" )
301 return toString( _MaxFloatPrec );
303 else
304 if( name == "negative_filter" )
306 std::string s;
307 s.reserve( _NegativeFilter.size() );
309 std::vector< u32char >::const_iterator itr;
310 for (itr = _NegativeFilter.begin(); itr != _NegativeFilter.end(); ++itr)
311 CUtfStringView::append(s, *itr);
313 return s;
315 else
316 return CInterfaceGroup::getProperty( name );
319 void CGroupEditBox::setProperty( const std::string &name, const std::string &value )
321 if( name == "onchange" )
323 _AHOnChange = value;
324 return;
326 else
327 if( name == "onchange_params" )
329 _ParamsOnChange = value;
330 return;
332 else
333 if( name == "on_focus_lost" )
335 _AHOnFocusLost = value;
336 return;
338 else
339 if( name == "on_focus_lost_params" )
341 _AHOnFocusLostParams = value;
342 return;
344 else
345 if( name == "on_focus" )
347 _AHOnFocus = value;
348 return;
350 else
351 if( name == "on_focus_params" )
353 _AHOnFocusParams = value;
354 return;
356 else
357 if( name == "max_num_chars" )
359 uint32 i;
360 if( fromString( value, i ) )
361 _MaxNumChar = i;
362 return;
364 else
365 if( name == "max_num_bytes" )
367 uint32 i;
368 if( fromString( value, i ) )
369 _MaxNumBytes = i;
370 return;
372 else
373 if( name == "max_num_return" )
375 uint32 i;
376 if( fromString( value, i ) )
377 _MaxNumReturn = i;
378 return;
380 else
381 if( name == "max_chars_size" )
383 sint32 i;
384 if( fromString( value, i ) )
385 _MaxCharsSize = i;
386 return;
388 else
389 if( name == "enter_loose_focus" )
391 bool b;
392 if( fromString( value, b ) )
393 _LooseFocusOnEnter = b;
394 return;
396 else
397 if( name == "enter_recover_focus" )
399 bool b;
400 if( fromString( value, b ) )
401 _RecoverFocusOnEnter = b;
402 return;
404 else
405 if( name == "prompt" )
407 _Prompt = CUtfStringView(value).toUtf32();
408 return;
410 else
411 if( name == "enter_type" )
413 if( value == "integer" )
414 _EntryType = Integer;
415 else
416 if( value == "positive_integer" )
417 _EntryType = PositiveInteger;
418 else
419 if( value == "float" )
420 _EntryType = Float;
421 else
422 if( value == "positive_float" )
423 _EntryType = PositiveFloat;
424 else
425 if( value == "alpha" )
426 _EntryType = Alpha;
427 else
428 if( value == "alpha_num" )
429 _EntryType = AlphaNum;
430 else
431 if( value == "alpha_num_space" )
432 _EntryType = AlphaNumSpace;
433 else
434 if( value == "password" )
435 _EntryType = Password;
436 else
437 if( value == "filename" )
438 _EntryType = Filename;
439 else
440 if( value == "playername" )
441 _EntryType = PlayerName;
443 return;
445 else
446 if( name == "menu_r" )
448 _ListMenuRight = value;
449 return;
451 else
452 if( name == "max_historic" )
454 uint32 i;
455 if( fromString( value, i ) )
456 _MaxHistoric = i;
457 return;
459 else
460 if( name == "backup_father_container_pos" )
462 bool b;
463 if( fromString( value, b ) )
464 _BackupFatherContainerPos = b;
465 return;
467 else
468 if( name == "want_return" )
470 bool b;
471 if( fromString( value, b ) )
472 _WantReturn = b;
473 return;
475 else
476 if( name == "clear_on_escape" )
478 bool b;
479 if( fromString( value, b ) )
480 _ClearOnEscape = b;
481 return;
483 else
484 if( name == "savable" )
486 bool b;
487 if( fromString( value, b ) )
488 _Savable = b;
489 return;
491 else
492 if( name == "max_float_prec" )
494 uint32 i;
495 if( fromString( value, i ) )
496 _MaxFloatPrec = i;
497 return;
499 else
500 if( name == "negative_filter" )
502 _NegativeFilter.clear();
503 ::u32string::size_type i;
504 ::u32string ustr = CUtfStringView(value).toUtf32();
505 for( i = 0; i < ustr.size(); i++ )
506 _NegativeFilter.push_back(ustr[i]);
508 else
509 CInterfaceGroup::setProperty( name, value );
512 xmlNodePtr CGroupEditBox::serialize( xmlNodePtr parentNode, const char *type ) const
514 xmlNodePtr node = CInterfaceGroup::serialize( parentNode, type );
515 if( node == NULL )
516 return NULL;
518 xmlSetProp( node, BAD_CAST "type", BAD_CAST "edit_box" );
519 xmlSetProp( node, BAD_CAST "onchange", BAD_CAST _AHOnChange.c_str() );
520 xmlSetProp( node, BAD_CAST "onchange_params", BAD_CAST _ParamsOnChange.c_str() );
521 xmlSetProp( node, BAD_CAST "on_focus_lost", BAD_CAST _AHOnFocusLost.c_str() );
522 xmlSetProp( node, BAD_CAST "on_focus_lost_params", BAD_CAST _AHOnFocusLostParams.c_str() );
523 xmlSetProp( node, BAD_CAST "on_focus", BAD_CAST _AHOnFocus.c_str() );
524 xmlSetProp( node, BAD_CAST "on_focus_params", BAD_CAST _AHOnFocusParams.c_str() );
525 xmlSetProp( node, BAD_CAST "max_num_chars", BAD_CAST toString( _MaxNumChar ).c_str() );
526 xmlSetProp( node, BAD_CAST "max_num_bytes", BAD_CAST toString( _MaxNumBytes ).c_str() );
527 xmlSetProp( node, BAD_CAST "max_num_return", BAD_CAST toString( _MaxNumReturn ).c_str() );
528 xmlSetProp( node, BAD_CAST "max_chars_size", BAD_CAST toString( _MaxCharsSize ).c_str() );
529 xmlSetProp( node, BAD_CAST "enter_loose_focus", BAD_CAST toString( _LooseFocusOnEnter ).c_str() );
530 xmlSetProp( node, BAD_CAST "enter_recover_focus", BAD_CAST toString( _RecoverFocusOnEnter ).c_str() );
531 xmlSetProp( node, BAD_CAST "prompt", BAD_CAST CUtfStringView(_Prompt).toUtf8().c_str() );
533 std::string e;
534 switch( _EntryType )
536 case Integer:
537 e = "integer";
538 break;
540 case PositiveInteger:
541 e = "positive_integer";
542 break;
544 case Float:
545 e = "float";
546 break;
548 case PositiveFloat:
549 e = "positive_float";
550 break;
552 case Alpha:
553 e = "alpha";
554 break;
556 case AlphaNum:
557 e = "alpha_num";
558 break;
560 case AlphaNumSpace:
561 e = "alpha_num_space";
562 break;
564 case Password:
565 e = "password";
566 break;
568 case Filename:
569 e = "filename";
570 break;
572 case PlayerName:
573 e = "playername";
574 break;
576 default:
577 break;
580 xmlSetProp( node, BAD_CAST "enter_type", BAD_CAST e.c_str() );
581 xmlSetProp( node, BAD_CAST "menu_r", BAD_CAST _ListMenuRight.c_str() );
582 xmlSetProp( node, BAD_CAST "max_historic", BAD_CAST toString( _MaxHistoric ).c_str() );
583 xmlSetProp( node, BAD_CAST "backup_father_container_pos",
584 BAD_CAST toString( _BackupFatherContainerPos ).c_str() );
585 xmlSetProp( node, BAD_CAST "want_return", BAD_CAST toString( _WantReturn ).c_str() );
586 xmlSetProp( node, BAD_CAST "clear_on_escape", BAD_CAST toString( _ClearOnEscape ).c_str() );
587 xmlSetProp( node, BAD_CAST "savable", BAD_CAST toString( _Savable ).c_str() );
588 xmlSetProp( node, BAD_CAST "max_float_prec", BAD_CAST toString( _MaxFloatPrec ).c_str() );
590 std::string s;
591 s.reserve( _NegativeFilter.size() );
593 std::vector< u32char >::const_iterator itr;
594 for( itr = _NegativeFilter.begin(); itr != _NegativeFilter.end(); ++itr )
595 CUtfStringView::append(s, *itr);
597 xmlSetProp( node, BAD_CAST "negative_filter", BAD_CAST s.c_str() );
599 return node;
602 // ----------------------------------------------------------------------------
603 bool CGroupEditBox::parse(xmlNodePtr cur, CInterfaceGroup * parentGroup)
605 if(!CInterfaceGroup::parse(cur, parentGroup))
606 return false;
607 CXMLAutoPtr prop;
609 if (! CInterfaceGroup::parse(cur,parentGroup) )
611 string tmp = "cannot parse view:"+getId()+", parent:"+parentGroup->getId();
612 nlinfo(tmp.c_str());
613 return false;
616 // NB: use InterfaceGroup "OnEnter" data. Different script params for an historic reason
617 CAHManager::getInstance()->parseAH(cur, "onenter", "params", _AHOnEnter, _AHOnEnterParams);
619 if( editorMode )
621 prop = (char*) xmlGetProp( cur, BAD_CAST "onenter" );
622 if (prop)
623 mapAHString( "onenter", std::string( (const char*)prop ) );
626 prop = (char*) xmlGetProp( cur, (xmlChar*)"onchange" );
627 if (prop) _AHOnChange = (const char *) prop;
628 prop = (char*) xmlGetProp( cur, (xmlChar*)"onchange_params" );
629 if (prop) _ParamsOnChange = (const char *) prop;
631 prop = (char*) xmlGetProp( cur, (xmlChar*)"on_focus_lost" );
632 if (prop) _AHOnFocusLost = (const char *) prop;
633 prop = (char*) xmlGetProp( cur, (xmlChar*)"on_focus_lost_params" );
634 if (prop) _AHOnFocusLostParams = (const char *) prop;
636 prop = (char*) xmlGetProp( cur, (xmlChar*)"on_focus" );
637 if (prop) _AHOnFocus = (const char *) prop;
638 prop = (char*) xmlGetProp( cur, (xmlChar*)"on_focus_params" );
639 if (prop) _AHOnFocusParams = (const char *) prop;
641 prop = (char*) xmlGetProp( cur, (xmlChar*)"max_num_chars" );
642 if (prop) fromString((const char*)prop, _MaxNumChar);
644 prop = (char*) xmlGetProp( cur, (xmlChar*)"max_num_bytes" );
645 if (prop) fromString((const char*)prop, _MaxNumBytes);
647 prop = (char*) xmlGetProp( cur, (xmlChar*)"max_num_return" );
648 if (prop) fromString((const char*)prop, _MaxNumReturn);
650 prop = (char*) xmlGetProp( cur, (xmlChar*)"max_chars_size" );
651 if (prop) fromString((const char*)prop, _MaxCharsSize);
653 prop = (char*) xmlGetProp( cur, (xmlChar*)"enter_loose_focus" );
654 if (prop) _LooseFocusOnEnter = convertBool(prop);
656 prop = (char*) xmlGetProp( cur, (xmlChar*)"enter_recover_focus" );
657 if (prop) _RecoverFocusOnEnter = convertBool(prop);
659 prop = (char*) xmlGetProp( cur, (xmlChar*)"reset_focus_on_hide" );
660 if (prop) _ResetFocusOnHide = convertBool(prop);
662 prop = (char*) xmlGetProp( cur, (xmlChar*)"prompt" );
663 if (prop) _Prompt = CUtfStringView((const char*)prop).toUtf32();
665 prop = (char*) xmlGetProp( cur, (xmlChar*)"entry_type" );
666 _EntryType = Text;
667 if (prop)
669 if (stricmp(prop, "text") == 0) _EntryType = Text;
670 else if (stricmp(prop, "integer") == 0) _EntryType = Integer;
671 else if (stricmp(prop, "positive_integer") == 0) _EntryType = PositiveInteger;
672 else if (stricmp(prop, "float") == 0) _EntryType = Float;
673 else if (stricmp(prop, "positive_float") == 0) _EntryType = PositiveFloat;
674 else if (stricmp(prop, "alpha") == 0) _EntryType = Alpha;
675 else if (stricmp(prop, "alpha_num") == 0) _EntryType = AlphaNum;
676 else if (stricmp(prop, "alpha_num_space") == 0) _EntryType = AlphaNumSpace;
677 else if (stricmp(prop, "password") == 0) _EntryType = Password;
678 else if (stricmp(prop, "filename") == 0) _EntryType = Filename;
679 else if (stricmp(prop, "playername") == 0) _EntryType = PlayerName;
680 else
681 nlwarning("<CGroupEditBox::parse> Unknown entry type %s", (const char *) prop);
684 prop = (char*) xmlGetProp( cur, (xmlChar*)"menu_r" );
685 if (prop) _ListMenuRight = toLowerAscii((const char *) prop);
687 prop = (char*) xmlGetProp( cur, (xmlChar*)"max_historic" );
688 if (prop) fromString((const char*)prop, _MaxHistoric);
690 prop = (char*) xmlGetProp( cur, (xmlChar*)"backup_father_container_pos" );
691 if (prop) _BackupFatherContainerPos = convertBool(prop);
693 prop = (char*) xmlGetProp( cur, (xmlChar*)"want_return" );
694 if (prop) _WantReturn = convertBool(prop);
696 prop = (char*) xmlGetProp( cur, (xmlChar*)"clear_on_escape" );
697 if (prop) _ClearOnEscape = convertBool(prop);
699 prop = (char*) xmlGetProp( cur, (xmlChar*)"savable" );
700 if (prop) _Savable = convertBool(prop);
702 // For float conversion only
703 prop = (char*) xmlGetProp( cur, (xmlChar*)"max_float_prec" );
704 if (prop)
706 fromString((const char*)prop, _MaxFloatPrec);
707 _MaxFloatPrec = min((uint32)20, _MaxFloatPrec);
710 // negative characters filter
711 prop = (char*) xmlGetProp( cur, (xmlChar*)"negative_filter" );
712 if (prop)
714 _NegativeFilter.clear();
715 ::u32string::size_type i;
716 ::u32string ustr = CUtfStringView(prop).toUtf32();
717 for( i = 0; i < ustr.size(); i++ )
718 _NegativeFilter.push_back(ustr[i]);
721 return true;
724 // ----------------------------------------------------------------------------
725 void CGroupEditBox::draw ()
728 CViewRenderer &rVR = *CViewRenderer::getInstance();
730 /*CRGBA col;
731 col.modulateFromColor(CRGBA(64,64,64,255), pIM->getGlobalColorForContent());
732 rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), col );
735 // Frozen? display text in "grayed"
736 CRGBA bkupTextColor;
737 if(_Frozen && _ViewText)
739 bkupTextColor= _ViewText->getColor();
740 CRGBA grayed= bkupTextColor;
741 grayed.A>>=2;
742 _ViewText->setColor(grayed);
745 // draw the group and thus the text
746 CInterfaceGroup::draw();
748 // restore the text color if changed
749 if(_Frozen && _ViewText)
750 _ViewText->setColor(bkupTextColor);
752 // no text setuped? abort
753 if (!_ViewText) return;
755 // Display the selection if needed
756 if (_CurrSelection == this && _SelectCursorPos!=_CursorPos)
758 sint32 blankTextId= rVR.getBlankTextureId();
759 CRGBA col= _BackSelectColor;
760 col.A= CWidgetManager::getInstance()->getGlobalColorForContent().A;
761 sint32 minPos= min(_CursorPos, _SelectCursorPos) + (sint32)_Prompt.length();
762 sint32 maxPos= max(_CursorPos, _SelectCursorPos) + (sint32)_Prompt.length();
764 // get its position on screen
765 float cxMinPos, cyMinPos;
766 float cxMaxPos, cyMaxPos;
767 float height;
768 _ViewText->getCharacterPositionFromIndex(minPos, false, cxMinPos, cyMinPos, height);
769 _ViewText->getCharacterPositionFromIndex(maxPos, false, cxMaxPos, cyMaxPos, height);
771 // Multiline selection if cy different!
772 if(cyMinPos!=cyMaxPos)
774 nlassert(cyMaxPos<cyMinPos);
775 // draw the 1st quad from the first line from min pos to end of line
776 rVR.drawRotFlipBitmap(_RenderLayer, _ViewText->getXReal() + cxMinPos, _ViewText->getYReal() + cyMinPos, _ViewText->getW()-cxMinPos, height, 0, 0, blankTextId, col);
778 // Draw One quad for all lines into the big selection (if any)
779 sint32 cyBigQuad= cyMaxPos+height;
780 if(cyBigQuad<cyMinPos)
782 rVR.drawRotFlipBitmap(_RenderLayer, _ViewText->getXReal(), _ViewText->getYReal() + cyBigQuad, _ViewText->getW(), cyMinPos-cyBigQuad, 0, 0, blankTextId, col);
785 // draw the 3rd quad from the last line from start of line to max pos
786 rVR.drawRotFlipBitmap(_RenderLayer, _ViewText->getXReal(), _ViewText->getYReal() + cyMaxPos, cxMaxPos, height, 0, 0, blankTextId, col);
788 else
790 rVR.drawRotFlipBitmap(_RenderLayer, _ViewText->getXReal() + cxMinPos, _ViewText->getYReal() + cyMinPos, cxMaxPos-cxMinPos, height, 0, 0, blankTextId, col);
793 // Draw The Selection String in black
794 CRGBA bkup= _ViewText->getColor();
795 _ViewText->setColor(_TextSelectColor);
796 _ViewText->enableStringSelection(minPos, maxPos);
797 // redraw just this string,clipped by the group
798 CInterfaceGroup::drawElement(_ViewText);
799 // bkup
800 _ViewText->setColor(bkup);
801 _ViewText->disableStringSelection();
804 // Display the cursor if needed
805 if (CWidgetManager::getInstance()->getCaptureKeyboard () == this)
807 const CWidgetManager::SInterfaceTimes &times = CWidgetManager::getInstance()->getInterfaceTimes();
809 _BlinkTime += ( static_cast< float >( times.frameDiffMs ) / 1000.0f );
810 if (_BlinkTime > 0.25f)
812 _BlinkTime = fmodf(_BlinkTime, 0.25f);
813 _BlinkState = !_BlinkState;
815 if (_BlinkState) // is the cursor shown ?
817 // get its position on screen
818 float cx, cy;
819 float height;
820 _ViewText->getCharacterPositionFromIndex(_CursorPos + (sint)_Prompt.length(), _CursorAtPreviousLineEnd, cx, cy, height);
821 // display the cursor
822 // get the texture for the cursor
823 if (_CursorTexID == -1)
825 _CursorTexID = rVR.getTextureIdFromName("text_cursor.tga");
826 sint32 dummyCursorHeight;
827 rVR.getTextureSizeFromId(_CursorTexID, _CursorWidth, dummyCursorHeight);
829 // draw in a layer after to be on TOP of the text
830 sint32 cursorx = std::max((sint) 0, (sint)(cx - (_CursorWidth >> 1)));
831 rVR.drawRotFlipBitmap(_RenderLayer+1, _ViewText->getXReal() + cursorx, _ViewText->getYReal() + cy, 3, height, 0, 0, _CursorTexID);
836 // ----------------------------------------------------------------------------
837 void CGroupEditBox::copy()
839 if (_CurrSelection != this)
841 nlwarning("Selection can only be on focus");
843 stopParentBlink();
845 // get the selection and copy it
846 if (CViewRenderer::getInstance()->getDriver()->copyTextToClipboard(getSelection()))
847 nlinfo ("Chat input was copied in the clipboard");
850 // ----------------------------------------------------------------------------
851 void CGroupEditBox::paste()
853 if(_CurrSelection != NULL)
855 if (_CurrSelection != this)
857 nlwarning("Selection can only be on focus");
859 cutSelection();
862 string sString;
864 if (CViewRenderer::getInstance()->getDriver()->pasteTextFromClipboard(sString))
866 // append string now
867 appendStringFromClipboard(sString);
871 // ----------------------------------------------------------------------------
872 void CGroupEditBox::appendStringFromClipboard(const std::string &str)
874 stopParentBlink();
875 makeTopWindow();
877 writeString(str, true, false);
878 nlinfo ("Chat input was pasted from the clipboard");
880 triggerOnChangeAH();
882 _CursorAtPreviousLineEnd = false;
885 // ----------------------------------------------------------------------------
886 void CGroupEditBox::writeString(const std::string &str16, bool replace, bool atEnd)
888 // For now, just trim unsupported codepoints to make emoji fallback to text form
889 ::u32string str = trimUnsupported(CUtfStringView(str16).toUtf32());
890 sint length = (sint)str.length();
892 ::u32string toAppend;
893 // filter character depending on the entry type
894 switch (_EntryType)
896 case Text:
897 case Password:
899 if (_NegativeFilter.empty())
901 toAppend = str;
903 else
905 for (sint k = 0; k < length; ++k)
907 if (!isFiltered(str[k]))
909 toAppend += str[k];
913 // remove '\r' characters
914 toAppend.erase(std::remove(toAppend.begin(), toAppend.end(), (u32char) '\r'), toAppend.end());
917 break;
918 case PositiveInteger:
919 case PositiveFloat:
921 for (sint k = 0; k < length; ++k)
923 if (isdigit(str[k]) || str[k]== ' ' ||
924 (_EntryType==PositiveFloat && str[k]=='.') )
926 if (!isFiltered(str[k]))
928 toAppend += str[k];
933 break;
934 case Integer:
935 case Float:
937 for (sint k = 0; k < length; ++k)
939 if (isdigit(str[k]) || str[k]== ' ' || str[k]== '-' ||
940 (_EntryType==Float && str[k]=='.') )
942 if (!isFiltered(str[k]))
944 toAppend += str[k];
949 break;
950 case AlphaNumSpace:
952 for (sint k = 0; k < length; ++k)
954 if (isValidAlphaNumSpace(str[k]))
956 if (!isFiltered(str[k]))
958 toAppend += str[k];
963 break;
964 case AlphaNum:
966 for (sint k = 0; k < length; ++k)
968 if (isValidAlphaNum(str[k]))
970 if (!isFiltered(str[k]))
972 toAppend += str[k];
977 break;
978 case Alpha:
980 for (sint k = 0; k < length; ++k)
982 if (isValidAlpha(str[k]))
984 if (!isFiltered(str[k]))
986 toAppend += str[k];
991 break;
992 case Filename:
994 for (sint k = 0; k < length; ++k)
996 if (isValidFilenameChar(str[k]))
998 if (!isFiltered(str[k]))
1000 toAppend += str[k];
1005 break;
1006 case PlayerName:
1008 for (sint k = 0; k < length; ++k)
1010 if (isValidPlayerNameChar(str[k]))
1012 if (!isFiltered(str[k]))
1014 toAppend += str[k];
1020 length = (sint)toAppend.size();
1021 if ((uint) (_InputString.length() + length) > _MaxNumChar)
1023 length = _MaxNumChar - (sint)_InputString.length();
1025 ::u32string toAdd = toAppend.substr(0, length);
1026 if (_MaxNumBytes && length)
1028 sint baseBytes = CUtfStringView(_InputString).toUtf8().length();
1029 sint bytes = baseBytes + CUtfStringView(toAdd).toUtf8().length();
1030 while (bytes > _MaxNumBytes)
1032 length -= std::max((int)(bytes - _MaxNumBytes) >> 2, 1);
1033 toAdd = toAdd.substr(0, length);
1034 bytes = baseBytes + CUtfStringView(toAdd).toUtf8().length();
1037 sint32 minPos;
1038 sint32 maxPos;
1039 if (_CurrSelection == this)
1041 minPos = min(_CursorPos, _SelectCursorPos);
1042 maxPos = max(_CursorPos, _SelectCursorPos);
1044 else
1046 minPos = _CursorPos;
1047 maxPos = _CursorPos;
1050 if (replace)
1052 _InputString = _InputString.substr(0, minPos) + toAdd + _InputString.substr(maxPos);
1053 _CursorPos = minPos+(sint32)toAdd.length();
1055 else
1057 if (atEnd)
1059 _InputString = _InputString.substr(0, maxPos) + toAdd + _InputString.substr(maxPos);
1060 _CursorPos = maxPos;
1061 _SelectCursorPos = _CursorPos;
1064 else
1066 _InputString = _InputString.substr(0, minPos) + toAdd + _InputString.substr(minPos);
1067 _CursorPos = minPos+(sint32)toAdd.length();
1068 _SelectCursorPos = maxPos+(sint32)toAdd.length();
1073 // ----------------------------------------------------------------------------
1074 void CGroupEditBox::handleEventChar(const NLGUI::CEventDescriptorKey &rEDK)
1076 stopParentBlink();
1077 switch(rEDK.getChar())
1079 case KeyESCAPE:
1080 _CurrentHistoricIndex= -1;
1081 // stop selection
1082 _CurrSelection = NULL;
1083 _CursorAtPreviousLineEnd = false;
1084 if (_ClearOnEscape)
1086 setInputStringRef(::u32string());
1087 triggerOnChangeAH();
1089 CWidgetManager::getInstance()->setCaptureKeyboard(NULL);
1090 break;
1091 case KeyTAB:
1092 makeTopWindow();
1093 break;
1094 // OTHER
1095 default:
1096 bool isKeyRETURN = !rEDK.getKeyCtrl() && rEDK.getChar() == KeyRETURN;
1097 if (isKeyRETURN && !_WantReturn)
1099 // update historic.
1100 if(_MaxHistoric)
1102 if( !_InputString.empty() )
1104 _Historic.push_front(_InputString);
1105 if(_Historic.size()>_MaxHistoric)
1106 _Historic.pop_back();
1108 _CurrentHistoricIndex= -1;
1111 _CursorPos = 0;
1112 // loose the keyboard focus
1113 if (NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:CHAT:ENTER_DONT_QUIT_CB")->getValue32() == 0)
1115 if(_LooseFocusOnEnter)
1116 CWidgetManager::getInstance()->setCaptureKeyboard(NULL);
1118 // stop selection
1119 _CurrSelection = NULL;
1120 _CursorAtPreviousLineEnd = false;
1121 CAHManager::getInstance()->runActionHandler(_AHOnEnter, this, _AHOnEnterParams);
1123 else
1125 // If the char is not alphanumeric -> return.
1126 // if(!isalnum(ec.Char))
1127 // return
1128 if( (rEDK.getChar()>=32) || isKeyRETURN )
1130 if (isKeyRETURN)
1132 //u32string copyStr= _InputString;
1133 //if ((uint) std::count(copyStr.begin(), copyStr.end(), '\n') >= _MaxNumReturn)
1134 if ((uint)std::count(_InputString.begin(), _InputString.end(), '\n') >= _MaxNumReturn)
1135 break;
1138 // if selection is activated, then cut the selection
1139 if(_CurrSelection != NULL)
1141 if (_CurrSelection != this)
1143 nlwarning("Selection can only be on focus");
1145 cutSelection();
1148 u32char c = isKeyRETURN ? '\n' : rEDK.getChar();
1149 if (isFiltered(c)) return;
1150 c = supportedCodepoint(c);
1151 if (!c) return; // For now, just trim unsupported codepoints to make emoji fallback to text form
1152 switch(_EntryType)
1154 case Integer:
1155 if (c > 255 || !(c =='-' || c==' ' || isdigit((int) c)))
1156 return;
1157 break;
1158 case PositiveInteger:
1159 if (c > 255 || !(c == ' ' || isdigit((int) c)))
1160 return;
1161 break;
1162 case Float:
1163 if (c > 255 || !(c =='-' || c==' ' || c=='.' || isdigit((int) c)))
1164 return;
1165 break;
1166 case PositiveFloat:
1167 if (c > 255 || !(c == ' ' || c=='.' || isdigit((int) c)))
1168 return;
1169 break;
1170 case AlphaNumSpace:
1171 if (!isValidAlphaNumSpace(c))
1172 return;
1173 break;
1174 case AlphaNum:
1175 if (!isValidAlphaNum(c))
1176 return;
1177 break;
1178 case Alpha:
1179 if (!isValidAlpha(c))
1180 return;
1181 break;
1182 case Filename:
1183 if (!isValidFilenameChar(c))
1184 return;
1185 break;
1186 case PlayerName:
1187 if (!isValidPlayerNameChar(c))
1188 return;
1189 break;
1190 default:
1191 break;
1193 // verify integer bounds
1194 if(_EntryType==Integer && (_IntegerMinValue!=INT_MIN || _IntegerMaxValue!=INT_MAX))
1196 // estimate new string
1197 ::u32string copyStr= _InputString;
1198 ::u32string::iterator it = copyStr.begin() + _CursorPos;
1199 copyStr.insert(it, c);
1200 sint32 value;
1201 fromString(CUtfStringView(copyStr).toUtf8(), value);
1202 // if out of bounds, abort char
1203 if(value<_IntegerMinValue || value>_IntegerMaxValue)
1204 return;
1206 // verify integer bounds
1207 if(_EntryType==PositiveInteger && (_PositiveIntegerMinValue!=0 || _PositiveIntegerMaxValue!=UINT_MAX))
1209 // estimate new string
1210 ::u32string copyStr= _InputString;
1211 ::u32string::iterator it = copyStr.begin() + _CursorPos;
1212 copyStr.insert(it, c);
1213 // \todo yoyo: this doesn't really work i think....
1214 uint32 value;
1215 fromString(CUtfStringView(copyStr).toUtf8(), value);
1216 // if out of bounds, abort char
1217 if(value<_PositiveIntegerMinValue || value>_PositiveIntegerMaxValue)
1218 return;
1220 // verify max num char
1221 if ((uint) _InputString.length() < _MaxNumChar)
1223 if (!_MaxNumBytes || CUtfStringView(_InputString + c).toUtf8().size() <= _MaxNumBytes)
1225 makeTopWindow();
1226 ::u32string::iterator it = _InputString.begin() + _CursorPos;
1227 _InputString.insert(it, c);
1228 ++_CursorPos;
1229 triggerOnChangeAH();
1232 if (isKeyRETURN)
1234 CAHManager::getInstance()->runActionHandler(_AHOnEnter, this, _AHOnEnterParams);
1237 _CursorAtPreviousLineEnd = false;
1239 break;
1243 // ----------------------------------------------------------------------------
1244 void CGroupEditBox::handleEventString(const NLGUI::CEventDescriptorKey &rEDK)
1246 appendStringFromClipboard(rEDK.getString());
1249 // ----------------------------------------------------------------------------
1250 bool CGroupEditBox::undo()
1252 if (CWidgetManager::getInstance()->getCaptureKeyboard() != this) return false;
1253 if (!_CanUndo) return false;
1254 _ModifiedInputString = _InputString;
1255 setInputStringRef(_StartInputString);
1256 _CanUndo = false;
1257 _CanRedo = true;
1258 setCursorPos((sint32)_InputString.length());
1259 setSelectionAll();
1260 return true;
1263 // ----------------------------------------------------------------------------
1264 bool CGroupEditBox::redo()
1266 if (CWidgetManager::getInstance()->getCaptureKeyboard() != this) return false;
1267 if (!_CanRedo) return false;
1268 setInputStringRef(_ModifiedInputString);
1269 _CanUndo = true;
1270 _CanRedo = false;
1271 setCursorPos((sint32)_InputString.length());
1272 setSelectionAll();
1273 return true;
1276 // ----------------------------------------------------------------------------
1277 void CGroupEditBox::triggerOnChangeAH()
1279 _CanUndo = true;
1280 _CanRedo = false;
1282 if (!_AHOnChange.empty())
1283 CAHManager::getInstance()->runActionHandler(_AHOnChange, this, _ParamsOnChange);
1287 // ----------------------------------------------------------------------------
1288 bool CGroupEditBox::expand()
1290 if ((_EntryType == Integer) ||
1291 (_EntryType == PositiveInteger) ||
1292 (_EntryType == Float) ||
1293 (_EntryType == PositiveFloat) ||
1294 (_EntryType == AlphaNumSpace) ||
1295 (_EntryType == AlphaNum) ||
1296 (_EntryType == Alpha) ||
1297 (_EntryType == Filename) ||
1298 (_EntryType == PlayerName)
1300 return false;
1302 if(!_InputString.empty())
1304 if (_InputString[0] == '/')
1306 makeTopWindow();
1307 // for french, deutsch and russian, be aware of unicode
1308 std::string command = CUtfStringView(_InputString.substr(1)).toUtf8();
1309 ICommand::expand(command);
1310 // then back to u32string
1311 _InputString = CUtfStringView('/' + command).toUtf32();
1312 _InputString = _InputString;
1313 _CursorPos = (sint32)_InputString.length();
1314 _CursorAtPreviousLineEnd = false;
1315 triggerOnChangeAH();
1316 return true;
1319 return false;
1322 // ----------------------------------------------------------------------------
1323 void CGroupEditBox::back()
1325 makeTopWindow();
1326 // if selection is activated and not same cursors pos, then cut the selection
1327 if(_CurrSelection != NULL && _CursorPos != _SelectCursorPos)
1329 if (_CurrSelection != this)
1331 nlwarning("Selection can only be on focus");
1333 cutSelection();
1334 _CursorAtPreviousLineEnd = false;
1336 // else delete last character
1337 else if(_InputString.size () > 0 && _CursorPos != 0)
1339 ::u32string::iterator it = _InputString.begin() + (_CursorPos - 1);
1340 _InputString.erase(it);
1341 -- _CursorPos;
1342 _CursorAtPreviousLineEnd = false;
1343 triggerOnChangeAH();
1345 // must stop selection in all case
1346 if (_CurrSelection)
1348 if (_CurrSelection != this)
1350 nlwarning("Selection can only be on focus");
1352 _CurrSelection = NULL;
1356 // ----------------------------------------------------------------------------
1357 bool CGroupEditBox::handleEvent (const NLGUI::CEventDescriptor& event)
1359 if (!_Active || !_ViewText)
1360 return false;
1361 if (event.getType() == NLGUI::CEventDescriptor::key)
1363 if (_BypassNextKey)
1365 _BypassNextKey = false;
1366 return true;
1368 ///////////////
1369 // KEY EVENT //
1370 ///////////////
1371 const NLGUI::CEventDescriptorKey &rEDK = (const NLGUI::CEventDescriptorKey&)event;
1372 switch(rEDK.getKeyEventType())
1374 case NLGUI::CEventDescriptorKey::keychar: handleEventChar(rEDK); break;
1375 case NLGUI::CEventDescriptorKey::keystring: handleEventString(rEDK); break;
1376 default: break;
1378 // update the text
1379 setInputStringRef(_InputString);
1381 // if event of type char or string, consider handle all of them
1382 if( rEDK.getKeyEventType()==NLGUI::CEventDescriptorKey::keychar || rEDK.getKeyEventType()==NLGUI::CEventDescriptorKey::keystring )
1383 return true;
1384 // Else filter the EventKeyDown AND EventKeyUp.
1385 else
1387 // Look into the input handler Manager if the key combo has to be considered as handled
1388 if( ( CGroupEditBox::comboKeyHandler != NULL ) && comboKeyHandler->isComboKeyChat(rEDK) )
1389 return true;
1390 else
1391 return false;
1394 else
1395 if (event.getType() == NLGUI::CEventDescriptor::mouse)
1397 /////////////////
1398 // MOUSE EVENT //
1399 /////////////////
1400 const NLGUI::CEventDescriptorMouse &eventDesc = (const NLGUI::CEventDescriptorMouse &)event;
1402 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouserightup)
1404 if (CWidgetManager::getInstance()->getCapturePointerRight() == this)
1406 CWidgetManager::getInstance()->setCapturePointerRight(NULL);
1407 if (!_ListMenuRight.empty())
1409 if (CCtrlDraggable::getDraggedSheet() == NULL)
1411 _MenuFather = this;
1412 CWidgetManager::getInstance()->enableModalWindow (this, _ListMenuRight);
1413 stopParentBlink();
1416 return true;
1418 else
1420 return false;
1424 // if click, and not frozen, then get the focus
1425 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouseleftup && !_Frozen)
1427 _SelectingText = false;
1428 if (_SelectCursorPos == _CursorPos)
1429 _CurrSelection = NULL;
1431 return true;
1434 if (!isIn(eventDesc.getX(), eventDesc.getY()))
1435 return false;
1437 // if click, and not frozen, then get the focus
1438 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouseleftdown && !_Frozen)
1440 if( getEditorMode() )
1442 return CViewBase::handleEvent( event );
1445 _SelectingText = true;
1446 stopParentBlink();
1447 CWidgetManager::getInstance()->setCaptureKeyboard (this);
1448 CWidgetManager::getInstance()->setCapturePointerLeft (this);
1449 // set the right cursor position
1450 uint newCurPos;
1451 bool cursorAtPreviousLineEnd;
1452 _ViewText->getCharacterIndexFromPosition(eventDesc.getX() - _ViewText->getXReal(), eventDesc.getY() - _ViewText->getYReal(), newCurPos, cursorAtPreviousLineEnd);
1453 _CursorAtPreviousLineEnd = cursorAtPreviousLineEnd;
1454 _CursorPos = newCurPos;
1455 _CursorPos -= (sint32)_Prompt.length();
1456 _CursorPos = std::max(_CursorPos, sint32(0));
1457 _SelectCursorPos = _CursorPos;
1458 _CurrSelection = NULL;
1460 return true;
1462 // if click, and not frozen, then get the focus
1463 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousemove && !_Frozen && _SelectingText)
1465 // set the right cursor position
1466 uint newCurPos;
1467 bool cursorAtPreviousLineEnd;
1468 _CurrSelection = this;
1469 _ViewText->getCharacterIndexFromPosition(eventDesc.getX() - _ViewText->getXReal(), eventDesc.getY() - _ViewText->getYReal(), newCurPos, cursorAtPreviousLineEnd);
1470 _SelectCursorPos = newCurPos;
1471 _SelectCursorPos -= (sint32)_Prompt.length();
1472 _SelectCursorPos = std::max(_SelectCursorPos, sint32(0));
1473 return true;
1476 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mouserightdown)
1478 CWidgetManager::getInstance()->setCapturePointerRight(this);
1479 return true;
1482 else
1484 //////////////////
1485 // SYSTEM EVENT //
1486 //////////////////
1487 const NLGUI::CEventDescriptorSystem &eventDesc = (const NLGUI::CEventDescriptorSystem &)event;
1488 if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::activecalledonparent)
1490 NLGUI::CEventDescriptorActiveCalledOnParent &activeEvent = (NLGUI::CEventDescriptorActiveCalledOnParent &) eventDesc;
1491 if (activeEvent.getActive() == false && _ResetFocusOnHide)
1493 CWidgetManager::getInstance()->resetCaptureKeyboard();
1494 // If a selection was shown, reset it
1495 if (_CurrSelection == this) _CurrSelection = NULL;
1497 CInterfaceGroup::handleEvent(activeEvent);
1501 return false;
1504 // ----------------------------------------------------------------------------
1505 void CGroupEditBox::setupDisplayText()
1507 if (_ViewText)
1509 std::string usTmp;
1510 if (_EntryType == Password)
1512 usTmp = CUtfStringView(_Prompt + ::u32string(_InputString.size(), 0x2022)).toUtf8();
1514 else
1516 usTmp = CUtfStringView(_Prompt + _InputString).toUtf8();
1518 _ViewText->setText (usTmp);
1522 // ----------------------------------------------------------------------------
1523 void CGroupEditBox::checkCoords()
1525 if( !editorMode )
1526 setupDisplayText();
1528 CInterfaceGroup::checkCoords();
1531 // ----------------------------------------------------------------------------
1532 void CGroupEditBox::updateCoords()
1534 if (!_Setupped)
1536 setup();
1537 _Setupped = true;
1539 CInterfaceGroup::updateCoords();
1541 if (_ViewText)
1543 bool bRecalc = false;
1545 // if the length of the view text exceed the max w we allow, cut it
1546 while (_ViewText->getWReal() > _MaxCharsSize)
1548 // Suppr last char
1549 _InputString = _InputString.substr(0, _InputString.size()-1);
1551 setupDisplayText();
1553 _ViewText->updateCoords();
1554 bRecalc = true;
1557 // if the length of the view text exceed our real size, ensure the Cursor position is at least visible
1558 sint32 viewTextNewX = _ViewText->getX();
1559 if (_ViewText->getWReal() > _WReal)
1561 // Check if cursor visible
1562 float xCursVT, xCurs, yTmp, hTmp;
1563 // Get the cursor pos from the BL of the viewtext
1564 _ViewText->getCharacterPositionFromIndex(_CursorPos+(sint)_Prompt.size(), false, xCursVT, yTmp, hTmp);
1565 // Get the cursor pos from the BL of the edit box
1566 xCurs = xCursVT - (_XReal - _ViewText->getXReal());
1567 // If the cursor is outside the edit box move the view text to show the cursor
1568 if (xCurs > _WReal)
1570 viewTextNewX = _ViewTextDeltaX - (xCursVT - _WReal);
1572 if (xCurs < 0)
1574 if ((xCursVT + xCurs) < 0)
1575 viewTextNewX = _ViewTextDeltaX;
1576 else
1577 viewTextNewX = _ViewTextDeltaX - (xCursVT + xCurs);
1579 if (_CursorPos == 0)
1581 viewTextNewX = _ViewTextDeltaX;
1584 else
1586 viewTextNewX = _ViewTextDeltaX;
1589 // if X position changed, must recompute
1590 if (viewTextNewX != _ViewText->getX())
1592 _ViewText->setX(viewTextNewX);
1593 bRecalc = true;
1596 // recompute position
1597 if (bRecalc)
1598 CInterfaceGroup::updateCoords();
1601 if (_BackupFatherContainerPos)
1603 CGroupContainerBase *gc = static_cast< CGroupContainerBase* >( getEnclosingContainer() );
1605 if (gc && !gc->getTouchFlag(true))
1608 if (_ViewText && _ViewText->getNumLine() <= 1)
1610 if (_PrevNumLine > 1)
1612 gc->restorePosition();
1613 CInterfaceGroup::updateCoords();
1616 else
1618 if (_PrevNumLine <= 1)
1620 gc->backupPosition();
1623 _PrevNumLine = _ViewText->getNumLine();
1625 else
1627 gc->backupPosition();
1632 // ----------------------------------------------------------------------------
1633 void CGroupEditBox::clearViews()
1635 CInterfaceGroup::clearViews();
1638 // ----------------------------------------------------------------------------
1640 void CGroupEditBox::createViewText()
1642 nlwarning("Interface: CGroupEditBox: text 'edit_text' missing or bad type");
1643 nlwarning( "Trying to create a new 'edit_text' for %s", getId().c_str() );
1644 _ViewText = dynamic_cast< CViewText* >( CInterfaceFactory::createClass( "text" ) );
1645 if( _ViewText == NULL )
1647 nlwarning( "Failed to create new 'edit_text' for %s", getId().c_str() );
1648 return;
1651 _ViewText->setParent( this );
1652 _ViewText->setIdRecurse( "edit_text" );
1653 _ViewText->setTextLocalized( "", false );
1654 _ViewText->setPosRef( Hotspot_ML );
1655 _ViewText->setParentPosRef( Hotspot_ML );
1656 addView( _ViewText );
1658 sint32 w,h;
1659 w = std::max( sint32( _ViewText->getFontWidth() * CUtfStringView(_ViewText->getText()).count() ), getW() );
1660 h = std::max( sint32( _ViewText->getFontHeight() ), getH() );
1662 setH( h );
1663 setW( w );
1666 // ----------------------------------------------------------------------------
1667 void CGroupEditBox::setup()
1669 // bind to the controls
1670 if( _ViewText == NULL )
1671 _ViewText = dynamic_cast<CViewText *>(CInterfaceGroup::getView("edit_text"));
1673 if(_ViewText == NULL)
1674 createViewText();
1676 _ViewText->setEditorSelectable( false );
1678 // For MultiLine editbox, clip the end space, else weird when edit space at end of line (nothing happens)
1679 if(_ViewText)
1680 _ViewText->setMultiLineClipEndSpace(true);
1682 // Bakcup the delta X of this view text
1683 if(_ViewText)
1684 _ViewTextDeltaX= _ViewText->getX();
1685 else
1686 _ViewTextDeltaX= 0;
1688 // read options
1689 CInterfaceOptions *pIO = CWidgetManager::getInstance()->getOptions("text_selection");
1690 if (pIO != NULL)
1692 _BackSelectColor= pIO->getValColor("back_select_color");
1693 _TextSelectColor= pIO->getValColor("text_select_color");
1698 // ----------------------------------------------------------------------------
1699 void CGroupEditBox::setPrompt(const std::string &s)
1701 _Prompt = CUtfStringView(s).toUtf32();
1705 // ----------------------------------------------------------------------------
1706 void CGroupEditBox::setInputString(const std::string &str)
1708 setInputStringRef(CUtfStringView(str).toUtf32());
1710 void CGroupEditBox::setInputStringRef(const ::u32string &str)
1712 _InputString = str;
1713 if (_CursorPos > (sint32) str.length())
1715 _CursorPos = (sint32)str.length();
1716 _CursorAtPreviousLineEnd = false;
1718 if (!_ViewText) return;
1719 setupDisplayText();
1721 invalidateCoords();
1725 // ***************************************************************************
1726 void CGroupEditBox::setDefaultInputString(const std::string &str)
1728 _DefaultInputString= true;
1729 setInputString(str);
1732 // ***************************************************************************
1733 sint32 CGroupEditBox::getInputStringAsInt() const
1735 sint32 value;
1736 fromString(getInputString(), value);
1737 return value;
1740 // ***************************************************************************
1741 void CGroupEditBox::setInputStringAsInt(sint32 val)
1743 setInputString(NLMISC::toString(val));
1746 // ***************************************************************************
1747 sint64 CGroupEditBox::getInputStringAsInt64() const
1749 sint64 value;
1750 fromString(getInputString(), value);
1751 return value;
1754 // ***************************************************************************
1755 void CGroupEditBox::setInputStringAsInt64(sint64 val)
1757 setInputString(NLMISC::toString(val));
1760 // ***************************************************************************
1761 float CGroupEditBox::getInputStringAsFloat() const
1763 float value;
1764 fromString(getInputString(), value);
1765 return value;
1768 // ***************************************************************************
1769 void CGroupEditBox::setInputStringAsFloat(float val)
1771 string fmt= "%." + NLMISC::toString(_MaxFloatPrec) + "f";
1772 setInputString(NLMISC::toString(fmt.c_str(), val));
1775 // ***************************************************************************
1776 void CGroupEditBox::cutSelection()
1778 ptrdiff_t minPos= min(_CursorPos, _SelectCursorPos);
1779 ptrdiff_t maxPos= max(_CursorPos, _SelectCursorPos);
1780 // cut the selection
1781 if(!_InputString.empty())
1783 _InputString= _InputString.substr(0, minPos) + _InputString.substr(maxPos);
1785 _CurrSelection = NULL;
1786 _CursorPos= minPos;
1787 triggerOnChangeAH();
1790 // ***************************************************************************
1791 std::string CGroupEditBox::getSelection()
1793 ptrdiff_t minPos= min(_CursorPos, _SelectCursorPos);
1794 ptrdiff_t maxPos= max(_CursorPos, _SelectCursorPos);
1795 // get the selection
1796 return CUtfStringView(_InputString.substr(minPos, maxPos-minPos)).toUtf8();
1801 // ***************************************************************************
1802 void CGroupEditBox::setSelectionAll()
1804 if(!_InputString.empty())
1806 _CurrSelection = this;
1807 _SelectCursorPos= 0;
1808 _CursorPos= (sint32)_InputString.size();
1809 _CursorAtPreviousLineEnd = false;
1813 // ***************************************************************************
1814 void CGroupEditBox::setActive(bool active)
1816 if (!active && _ResetFocusOnHide)
1817 CWidgetManager::getInstance()->resetCaptureKeyboard();
1819 CInterfaceGroup::setActive(active);
1822 // ***************************************************************************
1823 std::string CGroupEditBox::getPrompt() const
1825 return NLMISC::CUtfStringView(_Prompt).toUtf8();
1828 // ***************************************************************************
1829 std::string CGroupEditBox::getInputString() const
1831 return NLMISC::CUtfStringView(_InputString).toUtf8();
1834 #ifdef RYZOM_LUA_UCSTRING
1835 // ***************************************************************************
1836 void CGroupEditBox::setInputStringAsUtf16(const ucstring &str)
1838 setInputStringAsUtf32(CUtfStringView(str).toUtf32());
1841 // ***************************************************************************
1842 ucstring CGroupEditBox::getInputStringAsUtf16() const
1844 return CUtfStringView(_InputString).toUtf16();
1846 #endif
1848 // ***************************************************************************
1849 void CGroupEditBox::setCommand(const std::string &command, bool execute)
1851 // do nothing if frozen
1852 if(_Frozen)
1853 return;
1855 // set the string and maybe execute
1856 setInputString('/' + command);
1857 if (execute)
1859 // stop selection
1860 _CurrSelection = NULL;
1861 _CursorAtPreviousLineEnd = false;
1862 CAHManager::getInstance()->runActionHandler(_AHOnEnter, this, _AHOnEnterParams);
1864 else
1866 CWidgetManager::getInstance()->setCaptureKeyboard (this);
1867 _CursorPos = (sint32)_InputString.length();
1871 // ***************************************************************************
1872 void CGroupEditBox::makeTopWindow()
1874 CInterfaceGroup *root = getRootWindow();
1875 if(root)
1876 CWidgetManager::getInstance()->setTopWindow(root);
1879 // ***************************************************************************
1880 void CGroupEditBox::clearAllEditBox()
1882 _InputString.clear();
1883 _CursorPos = 0;
1884 _CursorAtPreviousLineEnd = false;
1885 if (!_ViewText) return;
1886 setupDisplayText();
1887 invalidateCoords();
1890 // ***************************************************************************
1891 sint32 CGroupEditBox::getMaxUsedW() const
1893 return _W;
1896 // ***************************************************************************
1897 sint32 CGroupEditBox::getMinUsedW() const
1899 return _W;
1902 // ***************************************************************************
1903 bool CGroupEditBox::wantSerialConfig() const
1905 return _Savable && !_InputString.empty();
1908 // ***************************************************************************
1909 void CGroupEditBox::serialConfig(NLMISC::IStream &f)
1911 uint version = f.serialVersion(1);
1912 if(_DefaultInputString) // Don't want to save the default input
1914 _DefaultInputString= false;
1915 _InputString.clear();
1917 if (version < 1)
1919 ucstring str; // Compatibility
1920 if (!f.isReading())
1921 str = CUtfStringView(_InputString).toUtf16();
1922 f.serial(str);
1923 if (f.isReading())
1924 _InputString = CUtfStringView(str).toUtf32();
1926 else
1928 std::string str;
1929 if (!f.isReading())
1930 str = CUtfStringView(_InputString).toUtf8();
1931 f.serial(str);
1932 if (f.isReading())
1933 _InputString = CUtfStringView(str).toUtf32();
1935 f.serial(_CursorPos);
1936 f.serial(_PrevNumLine);
1937 if (f.isReading())
1939 setInputStringRef(_InputString);
1942 // serial selection
1943 bool isSelected = (_CurrSelection == this);
1944 f.serial(isSelected);
1945 if (isSelected)
1947 _CurrSelection = this;
1948 f.serial(_SelectCursorPos);
1952 // ***************************************************************************
1953 void CGroupEditBox::onQuit()
1955 // clear the text and restore backup pos before final save
1956 setInputStringRef(::u32string());
1957 _CurrSelection = NULL;
1960 // ***************************************************************************
1961 void CGroupEditBox::onLoadConfig()
1963 // config is not saved when there's an empty string, so restore that default state.
1964 setInputStringRef(::u32string());
1965 _CurrSelection = NULL;
1966 _PrevNumLine = 1;
1970 // ***************************************************************************
1971 void CGroupEditBox::elementCaptured(CCtrlBase *capturedElement)
1973 // If the input string is the default one, then reset it
1974 if(capturedElement == this)
1976 if (_DefaultInputString)
1978 _DefaultInputString= false;
1979 setInputStringRef(::u32string());
1981 _CanRedo = false;
1982 _CanUndo = false;
1983 _StartInputString = _ModifiedInputString = _InputString;
1985 CInterfaceGroup::elementCaptured(capturedElement);
1988 // ***************************************************************************
1989 void CGroupEditBox::onKeyboardCaptureLost()
1991 if (!_AHOnFocusLost.empty())
1992 CAHManager::getInstance()->runActionHandler(_AHOnFocusLost, this, _AHOnFocusLostParams);
1996 // ***************************************************************************
1997 int CGroupEditBox::luaSetSelectionAll(CLuaState &ls)
1999 const char *funcName = "setSelectionAll";
2000 CLuaIHM::checkArgCount(ls, funcName, 0);
2001 setSelectionAll();
2002 return 0;
2005 // ***************************************************************************
2006 void CGroupEditBox::setFocusOnText()
2008 // do nothing if frozen
2009 if(_Frozen)
2010 return;
2012 // else set the focus
2013 CWidgetManager::getInstance()->setCaptureKeyboard (this);
2015 _CurrSelection = this;
2016 _SelectCursorPos= (sint32)_InputString.size();
2017 _CursorPos= (sint32)_InputString.size();
2018 _CursorAtPreviousLineEnd = false;
2021 // ***************************************************************************
2022 int CGroupEditBox::luaSetFocusOnText(CLuaState &ls)
2024 const char *funcName = "setFocusOnText";
2025 CLuaIHM::checkArgCount(ls, funcName, 0);
2027 setFocusOnText();
2029 return 0;
2032 // ***************************************************************************
2033 int CGroupEditBox::luaCancelFocusOnText(CLuaState &ls)
2035 const char *funcName = "cancelFocusOnText";
2036 CLuaIHM::checkArgCount(ls, funcName, 0);
2038 if (CWidgetManager::getInstance()->getCaptureKeyboard()==this || CWidgetManager::getInstance()->getOldCaptureKeyboard()==this)
2039 CWidgetManager::getInstance()->resetCaptureKeyboard();
2041 _CurrSelection = NULL;
2042 _SelectCursorPos= 0;
2043 _CursorPos= 0;
2044 _CursorAtPreviousLineEnd = false;
2046 return 0;
2049 // ***************************************************************************
2050 int CGroupEditBox::luaSetupDisplayText(CLuaState &ls)
2052 const char *funcName = "setupDisplayText";
2053 CLuaIHM::checkArgCount(ls, funcName, 0);
2054 setupDisplayText();
2055 return 0;
2058 // ***************************************************************************
2059 void CGroupEditBox::setColor(NLMISC::CRGBA col)
2061 if (_ViewText)
2062 _ViewText->setColor(col);
2065 // ***************************************************************************
2066 void CGroupEditBox::setFrozen (bool state)
2068 _Frozen= state;
2070 // if frozen, loose the focus
2071 if(_Frozen)
2073 // stop capture and selection
2074 CWidgetManager::getInstance()->setCaptureKeyboard (NULL);
2075 if(_CurrSelection==this) _CurrSelection = NULL;
2076 // do not allow to recover focus
2077 if (CWidgetManager::getInstance()->getOldCaptureKeyboard() == this)
2079 CWidgetManager::getInstance()->resetCaptureKeyboard();