Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / gui / view_text.cpp
blob73e8ce76c2f5acabb5ad34c99d57ef5927bb3da3
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 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) 2014-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/>.
21 #include "stdpch.h"
23 #include "nel/misc/bit_mem_stream.h"
24 #include "nel/misc/i18n.h"
26 #include "nel/gui/view_text.h"
27 #include "nel/gui/view_renderer.h"
28 #include "nel/gui/widget_manager.h"
29 #include "nel/gui/group_container_base.h"
30 #include "nel/gui/ctrl_tooltip.h"
31 #include "nel/misc/xml_auto_ptr.h"
32 #include "nel/gui/lua_ihm.h"
33 #include "nel/gui/view_pointer_base.h"
35 using namespace std;
36 using namespace NLMISC;
37 using namespace NL3D;
39 #ifdef DEBUG_NEW
40 #define new DEBUG_NEW
41 #endif
43 typedef std::string::size_type TCharPos; // index of a chracter in a string
45 REGISTER_UI_CLASS(CViewText)
47 namespace NLGUI
50 // ***************************************************************************
51 void CViewText::setupDefault ()
53 _ParentElm = NULL;
55 _CaseMode = CaseNormal;
56 _Underlined = false;
57 _StrikeThrough = false;
58 _ContinuousUpdate = false;
59 _Active = true;
60 _X = 0;
61 _Y = 0;
62 _W = 0;;
63 _H = 0;
64 _SizeRef = 0;
65 _SizeDivW = 10;
66 _SizeDivH = 10;
67 _ParentPosRef = Hotspot_BL;
68 _PosRef = Hotspot_BL;
70 _FontSize = 12 +
71 CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont ).getValSInt32();
72 _FontSizeCoef = true;
73 _FontName.clear();
74 _Embolden = false;
75 _Oblique = false;
76 _Color = CRGBA(255,255,255,255);
77 _Shadow = false;
78 _ShadowOutline = false;
79 _ShadowColor = CRGBA(0,0,0,255);
80 _ShadowX = 1;
81 _ShadowY = 1;
83 _MultiLine = false;
84 _TextMode = DontClipWord;
85 _MultiLineSpace = 8;
86 _LineMaxW = std::numeric_limits<sint32>::max();
87 _MultiLineMaxWOnly = false;
88 _MultiLineClipEndSpace = false;
89 _LastMultiLineMaxW = 0;
90 _MultiMinLine = 0;
91 _MultiMaxLine = 0;
92 _Index = 0xFFFFFFFF;
94 _Scale = CWidgetManager::getInstance()->getInterfaceScale();
95 _FontWidth= 0;
96 _FontHeight = 0;
97 _FontLegHeight = 0;
98 _TabWidth= 0;
100 _TextSelection= false;
101 _TextSelectionStart= 0;
102 _TextSelectionEnd= std::numeric_limits<uint>::max();
104 _InvalidTextContext= true;
105 _FirstLineX = 0;
107 _SingleLineTextClamped= false;
108 _OverExtendViewText= false;
109 _OverExtendViewTextUseParentRect= false;
111 _AutoClamp = false;
112 _ClampRight = true; // clamp on the right of the text
113 _OverflowText = "...";
115 _Localized = true;
117 _LetterColors = NULL;
118 _Setuped= false;
119 _AutoClampOffset = 0;
121 // Letter size
122 // - "_" that should be the character with the lowest part
123 // - A with an accent for the highest part
124 // https://www.compart.com/en/unicode/U+00C4
125 _FontSizingChars = "_\xC3\x84q";
126 // fallback if SizingChars are not supported by font
127 _FontSizingFallback = "|XO";
128 computeFontSize ();
131 // ***************************************************************************
133 NLMISC_REGISTER_OBJECT(CViewBase, CViewText, std::string, "text");
135 CViewText::CViewText(const TCtorParam &param)
136 :CViewBase(param)
138 setupDefault ();
140 CWidgetManager::getInstance()->registerInterfaceScaleWatcher(this);
143 ///constructor
144 // ***************************************************************************
145 CViewText:: CViewText (const std::string& id, const std::string Text, sint FontSize,
146 NLMISC::CRGBA Color, bool Shadow, bool ShadowOutline)
147 :CViewBase(TCtorParam())
149 _Id = id;
150 setupDefault ();
152 _FontSize = FontSize + CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont).getValSInt32();
153 _FontSizeCoef = true;
154 _Color = Color;
155 _Shadow = Shadow;
156 _ShadowOutline = ShadowOutline;
157 setHardText(Text);
158 computeFontSize ();
160 CWidgetManager::getInstance()->registerInterfaceScaleWatcher(this);
163 // ***************************************************************************
164 CViewText::~CViewText()
166 CWidgetManager::getInstance()->unregisterInterfaceScaleWatcher(this);
168 if (_Index != 0xFFFFFFFF)
169 CViewRenderer::getTextContext(_FontName)->erase (_Index);
170 clearLines();
172 if (!_Setuped)
173 for (uint i=0 ; i<_Tooltips.size() ; ++i)
174 delete _Tooltips[i];
176 _Tooltips.clear();
179 // ***************************************************************************
180 CViewText &CViewText::operator=(const CViewText &vt)
182 if (_Index != 0xFFFFFFFF)
183 CViewRenderer::getTextContext(_FontName)->erase (_Index);
185 // Create database entries
186 _Active = vt._Active;
187 _X = vt._X;
188 _Y = vt._Y;
189 _W = vt._W;
190 _H = vt._H;
191 _SizeRef = vt._SizeRef;
192 _SizeDivW = vt._SizeDivW;
193 _SizeDivH = vt._SizeDivH;
194 _ParentPosRef = vt._ParentPosRef;
195 _PosRef = vt._PosRef;
197 _FontSize = vt._FontSize;
198 _FontSizeCoef = vt._FontSizeCoef;
199 _Embolden = vt._Embolden;
200 _Oblique = vt._Oblique;
201 _Underlined = vt._Underlined;
202 _StrikeThrough = vt._StrikeThrough;
203 _Color = vt._Color;
204 _Shadow = vt._Shadow;
205 _ShadowOutline = vt._ShadowOutline;
206 _ShadowColor = vt._ShadowColor;
208 _MultiLine = false;
209 _MultiLineSpace = 8;
210 _LineMaxW= std::numeric_limits<sint32>::max();
211 _MultiLineMaxWOnly = false;
212 _MultiLineClipEndSpace = false;
213 _LastMultiLineMaxW = 0;
214 _Index = 0xFFFFFFFF;
216 _ModulateGlobalColor= vt._ModulateGlobalColor;
217 _Localized = vt._Localized;
219 // remove previous lines
220 clearLines();
221 _InvalidTextContext = true;
222 computeFontSize ();
224 return *this;
227 std::string CViewText::getProperty( const std::string &name ) const
229 std::string prop = getTextProperty( name );
231 if( !prop.empty() )
232 return prop;
233 else
234 if( name == "hardtext" )
236 return getHardText();
238 else
239 if( name == "hardtext_format" )
241 return _HardTextFormat;
243 else
244 return CViewBase::getProperty( name );
248 std::string CViewText::getTextProperty( const std::string &name ) const
250 if( name == "localize" )
252 return toString(_Localized);
254 else
255 if( name == "color" )
257 return toString( _Color );
259 else
260 if( name == "global_color" )
262 return toString( _ModulateGlobalColor );
264 else
265 if( name == "fontsize" )
267 if (_FontSizeCoef)
268 return toString(_FontSize - CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont ).getValSInt32());
270 return toString(_FontSize);
272 else
273 if ( name == "fontsize_coef" )
275 return toString(_FontSizeCoef);
277 else
278 if( name == "fontweight" )
280 if (_Embolden)
281 return "bold";
283 return "normal";
285 if( name == "fontstyle" )
287 if (_Oblique)
288 return "oblique";
290 return "normal";
292 else
293 if( name == "shadow" )
295 return toString( _Shadow );
297 else
298 if( name == "shadow_outline" )
300 return toString( _ShadowOutline );
302 else
303 if( name == "shadow_color" )
305 return toString( _ShadowColor );
307 else
308 if( name == "multi_line" )
310 return toString( _MultiLine );
312 else
313 if( name == "justification" )
315 switch( _TextMode )
317 case ClipWord:
318 return "clip_word";
319 break;
321 case DontClipWord:
322 return "dont_clip_word";
323 break;
325 case Justified:
326 return "justified";
327 break;
329 case Centered:
330 return "centered";
333 return "";
335 else
336 if( name == "line_maxw" )
338 return toString( _LineMaxW );
340 else
341 if( name == "multi_line_space" )
343 return toString( _MultiLineSpace );
345 else
346 if( name == "multi_line_maxw_only" )
348 return toString( _MultiLineMaxWOnly );
350 else
351 if( name == "multi_max_line" )
353 return toString( _MultiMaxLine );
355 else
356 if( name == "multi_min_line" )
358 return toString( _MultiMinLine );
360 else
361 if( name == "underlined" )
363 return toString( _Underlined );
365 else
366 if( name == "strikethrough" )
368 return toString( _StrikeThrough );
370 else
371 if( name == "case_mode" )
373 return toString( uint32( _CaseMode ) );
375 else
376 if( name == "over_extend_view_text" )
378 return toString( _OverExtendViewText );
380 else
381 if( name == "over_extend_parent_rect" )
383 return toString( _OverExtendViewTextUseParentRect );
385 else
386 if( name == "auto_clamp" )
388 return toString( _AutoClamp );
390 else
391 if( name == "clamp_right" )
393 return toString( _ClampRight );
395 else
396 if( name == "auto_clamp_offset" )
398 return toString( _AutoClampOffset );
400 else
401 if( name == "continuous_update" )
403 return toString( _ContinuousUpdate );
405 else
406 if ( name == "sizing_chars" )
408 return _FontSizingChars;
410 else
411 if ( name == "sizing_fallback" )
413 return _FontSizingFallback;
415 else
416 return "";
419 void CViewText::setProperty( const std::string &name, const std::string &value )
421 if( setTextProperty( name, value ) )
422 invalidateContent();
423 else
424 CViewBase::setProperty( name, value );
427 bool CViewText::setTextProperty( const std::string &name, const std::string &value )
429 if( name == "localize" )
431 bool b;
432 if (fromString(value, b))
434 _Localized = b;
435 setTextLocalized(_HardText.empty() ? _Text : _HardText); // FIXME: setCase?
436 _TextLength = 0;
438 return true;
440 else
441 if( name == "color" )
443 CRGBA c;
444 if( fromString( value, c ) )
445 _Color = c;
446 return true;
448 else
449 if( name == "global_color" )
451 bool b;
452 if( fromString( value, b ) )
453 _ModulateGlobalColor = b;
454 return true;
456 else
457 if( name == "fontsize" )
459 sint i;
460 if( fromString( value, i ) )
461 _FontSize = i + CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont ).getValSInt32();
462 return true;
464 else
465 if( name == "fontsize_coef" )
467 bool b;
468 bool oldValue = _FontSizeCoef;
469 if (fromString( value, b) )
470 _FontSizeCoef = b;
471 // must only change font size when current state changes
472 if (_FontSizeCoef != oldValue)
474 if (_FontSizeCoef)
475 _FontSize += CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont ).getValSInt32();
476 else
477 _FontSize -= CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont ).getValSInt32();
480 else
481 if( name == "fontweight" )
483 if (value == "bold")
484 _Embolden = true;
486 return true;
488 if( name == "fontstyle" )
490 if( value == "oblique" )
491 _Oblique = true;
492 return true;
494 else
495 if( name == "shadow" )
497 bool b;
498 if( fromString( value, b ) )
499 _Shadow = b;
500 return true;
502 else
503 if( name == "shadow_outline" )
505 bool b;
506 if( fromString( value, b ) )
507 _ShadowOutline = b;
508 return true;
510 else
511 if( name == "shadow_color" )
513 CRGBA c;
514 if( fromString( value, c ) )
515 _ShadowColor = c;
516 return true;
518 else
519 if( name == "shadow_x" )
521 sint sx;
522 if( fromString( value, sx ) )
523 _ShadowX = sx;
524 return true;
526 else
527 if( name == "shadow_y" )
529 sint sy;
530 if( fromString( value, sy ) )
531 _ShadowY = sy;
532 return true;
534 else
535 if( name == "multi_line" )
537 bool b;
538 if( fromString( value, b ) )
539 _MultiLine = b;
540 return true;
542 else
543 if( name == "justification" )
545 if( value == "clip_word" )
546 _TextMode = ClipWord;
547 else
548 if( value == "dont_clip_word" )
549 _TextMode = DontClipWord;
550 else
551 if( value == "justified" )
552 _TextMode = Justified;
553 else
554 if( value == "centered" )
555 _TextMode = Centered;
557 return true;
559 else
560 if( name == "line_maxw" )
562 sint32 i;
563 if( fromString( value, i ) )
564 _LineMaxW = i;
565 return true;
567 else
568 if( name == "multi_line_space" )
570 sint i;
571 if( fromString( value, i ) )
572 _MultiLineSpace = i;
573 return true;
575 else
576 if( name == "multi_line_maxw_only" )
578 bool b;
579 if( fromString( value, b ) )
580 _MultiLineMaxWOnly = b;
581 return true;
583 else
584 if( name == "multi_max_line" )
586 uint32 i;
587 if( fromString( value, i ) )
588 _MultiMaxLine = i;
589 return true;
591 else
592 if( name == "multi_min_line" )
594 uint32 i;
595 if( fromString( value, i ) )
596 _MultiMinLine = i;
597 return true;
599 else
600 if( name == "underlined" )
602 bool b;
603 if( fromString( value, b ) )
604 _Underlined = b;
605 return true;
607 else
608 if( name == "strikethrough" )
610 bool b;
611 if( fromString( value, b ) )
612 _StrikeThrough = b;
613 return true;
615 else
616 if( name == "case_mode" )
618 uint32 i;
619 if( fromString( value, i ) )
620 _CaseMode = (TCaseMode)i;
621 return true;
623 else
624 if( name == "over_extend_view_text" )
626 bool b;
627 if( fromString( value, b ) )
628 _OverExtendViewText = b;
629 return true;
631 else
632 if( name == "over_extend_parent_rect" )
634 bool b;
635 if( fromString( value, b ) )
636 _OverExtendViewTextUseParentRect = b;
637 return true;
639 else
640 if( name == "auto_clamp" )
642 bool b;
643 if( fromString( value, b ) )
644 _AutoClamp = b;
645 return true;
647 else
648 if( name == "clamp_right" )
650 bool b;
651 if( fromString( value, b ) )
652 _ClampRight = b;
653 return true;
655 else
656 if( name == "auto_clamp_offset" )
658 uint8 i;
659 if( fromString( value, i ) )
660 _AutoClampOffset = i;
661 return true;
663 else
664 if( name == "continuous_update" )
666 bool b;
667 if( fromString( value, b ) )
668 _ContinuousUpdate = b;
669 return true;
671 else
672 if( name == "text" )
674 setTextLocalized(value); // FIXME: setCase?
675 _TextLength = 0;
676 invalidateContent();
677 return true;
679 else
680 if( name == "hardtext" )
682 _Localized = true;
683 setTextLocalized(value); // FIXME: setCase?
684 _TextLength = 0;
685 invalidateContent();
686 return true;
688 else
689 if( name == "hardtext_format" )
691 _HardTextFormat = value;
693 if( _MultiLine )
694 setTextFormatTaged( _HardTextFormat );
695 else
696 setSingleLineTextFormatTaged( _HardTextFormat );
698 return true;
700 else
701 if( name == "sizing_chars" )
703 _FontSizingChars = value;
704 return true;
706 else
707 if( name == "sizing_fallback" )
709 _FontSizingFallback = value;
710 return true;
712 else
713 return false;
717 bool CViewText::serializeTextOptions( xmlNodePtr node ) const
719 xmlSetProp( node, BAD_CAST "localize", BAD_CAST toString( _Localized ).c_str() );
721 xmlSetProp( node, BAD_CAST "color", BAD_CAST toString( _Color ).c_str() );
722 xmlSetProp( node, BAD_CAST "global_color", BAD_CAST toString( _ModulateGlobalColor ).c_str() );
724 sint32 fontSize = _FontSize;
725 if (_FontSizeCoef) fontSize -= CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont ).getValSInt32();
726 xmlSetProp( node, BAD_CAST "fontsize", BAD_CAST toString(fontSize).c_str() );
727 xmlSetProp( node, BAD_CAST "fontsize_coef", BAD_CAST toString(_FontSizeCoef).c_str() );
729 std::string fontweight("normal");
730 if (_Embolden)
731 fontweight = "bold";
732 xmlSetProp( node, BAD_CAST "fontweight", BAD_CAST fontweight.c_str() );
734 std::string fontstyle("normal");
735 if (_Oblique)
736 fontstyle = "oblique";
737 xmlSetProp( node, BAD_CAST "fontstyle", BAD_CAST fontstyle.c_str() );
739 xmlSetProp( node, BAD_CAST "shadow", BAD_CAST toString( _Shadow ).c_str() );
740 xmlSetProp( node, BAD_CAST "shadow_outline", BAD_CAST toString( _ShadowOutline ).c_str() );
741 xmlSetProp( node, BAD_CAST "shadow_color", BAD_CAST toString( _ShadowColor ).c_str() );
742 xmlSetProp( node, BAD_CAST "shadow_x", BAD_CAST toString( _ShadowX ).c_str() );
743 xmlSetProp( node, BAD_CAST "shadow_y", BAD_CAST toString( _ShadowY ).c_str() );
744 xmlSetProp( node, BAD_CAST "multi_line", BAD_CAST toString( _MultiLine ).c_str() );
746 std::string just;
748 switch( _TextMode )
750 case ClipWord:
751 just = "clip_word";
752 break;
754 case DontClipWord:
755 just = "dont_clip_word";
756 break;
758 case Justified:
759 just = "justified";
760 break;
762 case Centered:
763 just = "centered";
764 break;
767 xmlSetProp( node, BAD_CAST "justification", BAD_CAST just.c_str() );
768 xmlSetProp( node, BAD_CAST "line_maxw", BAD_CAST toString( _LineMaxW ).c_str() );
769 xmlSetProp( node, BAD_CAST "multi_line_space", BAD_CAST toString( _MultiLineSpace ).c_str() );
770 xmlSetProp( node, BAD_CAST "multi_line_maxw_only", BAD_CAST toString( _MultiLineMaxWOnly ).c_str() );
771 xmlSetProp( node, BAD_CAST "multi_max_line", BAD_CAST toString( _MultiMaxLine ).c_str() );
772 xmlSetProp( node, BAD_CAST "multi_min_line", BAD_CAST toString( _MultiMinLine ).c_str() );
773 xmlSetProp( node, BAD_CAST "underlined", BAD_CAST toString( _Underlined ).c_str() );
774 xmlSetProp( node, BAD_CAST "strikethrough", BAD_CAST toString( _StrikeThrough ).c_str() );
775 xmlSetProp( node, BAD_CAST "case_mode", BAD_CAST toString( uint32( _CaseMode ) ).c_str() );
776 xmlSetProp( node, BAD_CAST "over_extend_view_text", BAD_CAST toString( _OverExtendViewText ).c_str() );
777 xmlSetProp( node, BAD_CAST "over_extend_parent_rect",
778 BAD_CAST toString( _OverExtendViewTextUseParentRect ).c_str() );
779 xmlSetProp( node, BAD_CAST "auto_clamp", BAD_CAST toString( _AutoClamp ).c_str() );
780 xmlSetProp( node, BAD_CAST "clamp_right", BAD_CAST toString( _ClampRight ).c_str() );
781 xmlSetProp( node, BAD_CAST "auto_clamp_offset", BAD_CAST toString( _AutoClampOffset ).c_str() );
782 xmlSetProp( node, BAD_CAST "continuous_update", BAD_CAST toString( _ContinuousUpdate ).c_str() );
783 xmlSetProp( node, BAD_CAST "sizing_chars", BAD_CAST _FontSizingChars.c_str() );
784 xmlSetProp( node, BAD_CAST "sizing_fallback", BAD_CAST _FontSizingFallback.c_str() );
786 return true;
790 xmlNodePtr CViewText::serialize( xmlNodePtr parentNode, const char *type ) const
792 xmlNodePtr node = CViewBase::serialize( parentNode, type );
793 if( node == NULL )
794 return NULL;
796 xmlSetProp( node, BAD_CAST "type", BAD_CAST "text" );
798 serializeTextOptions( node );
800 xmlSetProp( node, BAD_CAST "text", BAD_CAST (_HardText.empty() ? _Text.c_str() : _HardText.c_str()) );
801 xmlSetProp( node, BAD_CAST "hardtext_format", BAD_CAST _HardTextFormat.c_str() );
803 return node;
806 // ***************************************************************************
807 void CViewText::parseTextOptions (xmlNodePtr cur)
809 CXMLAutoPtr prop;
811 prop = xmlGetProp (cur, (xmlChar*)"localize");
812 if (prop) _Localized = convertBool((const char*)prop);
814 prop= (char*) xmlGetProp( cur, (xmlChar*)"color" );
815 _Color = CRGBA(255,255,255,255);
816 if (prop)
817 _Color = convertColor(prop);
819 prop= (char*) xmlGetProp (cur, (xmlChar*)"global_color");
820 if(prop)
821 _ModulateGlobalColor= convertBool(prop);
823 prop = (char*) xmlGetProp( cur, (xmlChar*)"fontsize" );
824 _FontSize = 12 + CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont).getValSInt32();
825 if (prop)
827 fromString((const char*)prop, _FontSize);
828 _FontSize += CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont).getValSInt32();
831 prop = (char*) xmlGetProp( cur, (xmlChar*)"fontsize_coef" );
832 _FontSizeCoef = true;
833 if (prop)
835 _FontSizeCoef = convertBool(prop);
836 if (!_FontSizeCoef)
837 _FontSize -= CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont).getValSInt32();
841 prop = (char*) xmlGetProp( cur, (xmlChar*)"fontweight" );
842 _Embolden = false;
843 if (prop)
845 if (nlstricmp("bold", (const char*)prop) == 0) _Embolden = true;
846 else nlwarning("<CViewText::parse> bad fontweight '%s'", (const char *)prop);
849 prop = (char*) xmlGetProp( cur, (xmlChar*)"fontstyle" );
850 _Oblique = false;
851 if (prop)
853 if (nlstricmp("oblique", (const char *) prop) == 0) _Oblique = true;
854 else nlwarning("<CViewText::parse> bad fontstyle '%s'", (const char *)prop);
857 prop = (char*) xmlGetProp( cur, (xmlChar*)"shadow" );
858 _Shadow = false;
859 if (prop)
860 _Shadow = convertBool(prop);
862 prop = (char*) xmlGetProp( cur, (xmlChar*)"shadow_outline" );
863 _ShadowOutline = false;
864 if (prop)
865 _ShadowOutline = convertBool(prop);
867 prop= (char*) xmlGetProp( cur, (xmlChar*)"shadow_color" );
868 _ShadowColor = CRGBA(0,0,0,255);
869 if (prop)
870 _ShadowColor = convertColor(prop);
872 prop= (char*) xmlGetProp( cur, (xmlChar*)"shadow_x" );
873 _ShadowX = 1;
874 if (prop)
875 fromString( (const char *)prop, _ShadowX);
877 prop= (char*) xmlGetProp( cur, (xmlChar*)"shadow_y" );
878 _ShadowY = 1;
879 if (prop)
880 fromString( (const char *)prop, _ShadowY);
882 prop = (char*) xmlGetProp( cur, (xmlChar*)"multi_line" );
883 _MultiLine = false;
884 if (prop)
885 _MultiLine = convertBool(prop);
887 prop = (char*) xmlGetProp( cur, (xmlChar*)"justification" );
888 if (prop)
890 if (nlstricmp("clip_word", (const char *) prop) == 0) _TextMode = ClipWord;
891 else if (nlstricmp("dont_clip_word", (const char *) prop) == 0) _TextMode = DontClipWord;
892 else if (nlstricmp("justified", (const char *) prop) == 0) _TextMode = Justified;
893 else if (nlstricmp("centered", (const char *) prop) == 0) _TextMode = Centered;
894 else nlwarning("<CViewText::parse> bad text mode");
897 prop = (char*) xmlGetProp( cur, (xmlChar*)"line_maxw" );
898 _LineMaxW = std::numeric_limits<sint32>::max();
899 if (prop)
900 fromString((const char*)prop, _LineMaxW);
902 prop = (char*) xmlGetProp( cur, (xmlChar*)"multi_line_space" );
903 _MultiLineSpace = 8;
904 if (prop)
905 fromString((const char*)prop, _MultiLineSpace);
907 prop = (char*) xmlGetProp( cur, (xmlChar*)"multi_line_maxw_only" );
908 _MultiLineMaxWOnly = false;
909 if (prop)
910 _MultiLineMaxWOnly = convertBool(prop);
912 prop = (char*) xmlGetProp( cur, (xmlChar*)"multi_max_line" );
913 _MultiMaxLine = 0;
914 if (prop)
915 fromString((const char*)prop, _MultiMaxLine);
917 prop = (char*) xmlGetProp( cur, (xmlChar*)"multi_min_line" );
918 _MultiMinLine = 0;
919 if (prop)
920 fromString((const char*)prop, _MultiMinLine);
922 prop = (char*) xmlGetProp( cur, (xmlChar*)"underlined" );
923 _Underlined = false;
924 if (prop)
925 _Underlined = convertBool(prop);
927 prop = (char*) xmlGetProp( cur, (xmlChar*)"strikethrough" );
928 _StrikeThrough = false;
929 if (prop)
930 _StrikeThrough = convertBool(prop);
932 prop = (char*) xmlGetProp( cur, (xmlChar*)"case_mode" );
933 _CaseMode = CaseNormal;
934 if (prop)
936 sint32 caseMode;
937 fromString((const char*)prop, caseMode);
938 _CaseMode = (TCaseMode)caseMode;
941 prop = (char*) xmlGetProp( cur, (xmlChar*)"over_extend_view_text" );
942 _OverExtendViewText= false;
943 if(prop)
944 _OverExtendViewText= convertBool(prop);
946 prop = (char*) xmlGetProp( cur, (xmlChar*)"over_extend_parent_rect" );
947 _OverExtendViewTextUseParentRect= false;
948 if(prop)
949 _OverExtendViewTextUseParentRect= convertBool(prop);
951 prop = (char*) xmlGetProp( cur, (xmlChar*)"auto_clamp" );
952 _AutoClamp = false;
953 if (prop)
954 _AutoClamp = convertBool(prop);
956 prop = (char*) xmlGetProp( cur, (xmlChar*)"clamp_right" );
957 _ClampRight = true;
958 if (prop)
959 _ClampRight = convertBool(prop);
961 prop = (char*) xmlGetProp( cur, (xmlChar*)"auto_clamp_offset" );
962 _AutoClampOffset = 0;
963 if (prop)
964 fromString((const char*)prop, _AutoClampOffset);
966 prop = (char*) xmlGetProp( cur, (xmlChar*)"continuous_update" );
967 if (prop)
969 _ContinuousUpdate = convertBool(prop);
972 // "_Ä" lowest/highest chars (underscore, A+diaeresis)
973 prop = (char*) xmlGetProp( cur, (xmlChar*)"sizing_chars" );
974 if (prop)
975 _FontSizingChars = (const char*)prop;
976 else
977 _FontSizingChars = "_\xC3\x84q";
979 // fallback if SizingChars are not supported by font
980 prop = (char*) xmlGetProp( cur, (xmlChar*)"sizing_fallback" );
981 if (prop)
982 _FontSizingFallback = (const char *)prop;
983 else
984 _FontSizingFallback = "|XO";
986 computeFontSize ();
990 * parse an xml node and initialize the base view mambers. Must call CViewBase::parse
991 * \param cur : pointer to the xml node to be parsed
992 * \param parentGroup : the parent group of the view
993 * \partam id : a refence to the string that will receive the view ID
994 * \return true if success
996 // ***************************************************************************
997 bool CViewText::parse(xmlNodePtr cur, CInterfaceGroup * parentGroup)
1000 CXMLAutoPtr prop;
1001 //try to get props that can be inherited from groups
1002 //if a property is not defined, try to find it in the parent group.
1003 //if it is undefined, set it to zero
1005 if (! CViewBase::parse(cur,parentGroup) )
1006 return false;
1008 //set w and h to 0 : they depend on the string contained
1009 _W = 0;
1010 _H = 0;
1012 //try to get the NEEDED specific props
1013 parseTextOptions(cur);
1015 prop = (char*) xmlGetProp( cur, (xmlChar*)"hardtext" );
1016 if (prop)
1018 const char *propPtr = prop;
1019 _Localized = true;
1020 setTextLocalized(propPtr);
1021 setCase(_Text, _CaseMode);
1022 _TextLength = 0;
1025 prop = (char*) xmlGetProp( cur, (xmlChar*)"hardtext_format" );
1026 if (prop)
1028 const char *propPtr = prop;
1029 _HardTextFormat = propPtr;
1031 if (_MultiLine)
1033 if (NLMISC::startsWith(propPtr, "u:"))
1034 setTextFormatTaged(std::string(propPtr).substr(2));
1035 else
1036 setTextFormatTaged(CI18N::get(propPtr));
1038 else
1040 setSingleLineTextFormatTaged(CI18N::get(propPtr));
1044 invalidateContent ();
1046 return true;
1049 // ***************************************************************************
1050 sint CViewText::getCurrentMultiLineMaxW() const
1052 if(_MultiLineMaxWOnly)
1053 return _LineMaxW;
1054 else
1056 sint offset = (sint)_XReal - (sint)_Parent->getXReal();
1057 return std::min(_Parent->getInnerWidth() - offset, (sint)_LineMaxW);
1062 // ***************************************************************************
1063 void CViewText::checkCoords ()
1065 if ((_MultiLine)&&(_Parent != NULL))
1067 // If never setuped, and if text is not empty
1068 if (_Lines.empty() && !_Text.empty())
1069 invalidateContent ();
1071 sint currentMaxW= getCurrentMultiLineMaxW();
1072 if ( _LastMultiLineMaxW != currentMaxW )
1074 if (_ContinuousUpdate)
1076 _LastMultiLineMaxW = currentMaxW;
1077 invalidateContent();
1079 else
1081 CCtrlBase *pCB = CWidgetManager::getInstance()->getCapturePointerLeft();
1082 if (pCB != NULL)
1084 if( pCB->isResizer() )
1086 // We are resizing !!!!
1088 else
1090 _LastMultiLineMaxW = currentMaxW;
1091 invalidateContent();
1094 else
1096 _LastMultiLineMaxW = currentMaxW;
1097 invalidateContent();
1102 else
1104 if (_Index == 0xFFFFFFFF)
1105 invalidateContent ();
1109 * draw the view
1111 // ***************************************************************************
1112 void CViewText::draw ()
1114 H_AUTO( RZ_Interface_CViewText_draw )
1116 CViewRenderer &rVR = *CViewRenderer::getInstance();
1118 #if 0
1119 //rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(64,64,64,255));
1121 // debug text with mouse hover
1122 if(CWidgetManager::getInstance()->getPointer())
1124 // but must check first if mouse is over
1125 sint32 x = CWidgetManager::getInstance()->getPointer()->getX();
1126 sint32 y = CWidgetManager::getInstance()->getPointer()->getY();
1127 bool mouseIn;
1128 // use parent clip or self clip?
1129 if(_OverExtendViewTextUseParentRect)
1130 mouseIn= _Parent && _Parent->isIn(x,y);
1131 else
1132 mouseIn= isIn(x,y);
1133 // if the mouse cursor is in the clip area
1134 if(mouseIn)
1136 rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, 1, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255));
1137 rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal+_HReal, _WReal, 1, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255));
1138 rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, 1, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255));
1139 rVR.drawRotFlipBitmap (_RenderLayer, _XReal+_WReal, _YReal, 1, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255));
1142 #endif
1144 // *** Out Of Clip?
1145 sint32 ClipX, ClipY, ClipW, ClipH;
1146 rVR.getClipWindow (ClipX, ClipY, ClipW, ClipH);
1147 if (((_XReal) > (ClipX+ClipW)) || ((_XReal+_WReal) < ClipX) ||
1148 ((_YReal) > (ClipY+ClipH)) || ((_YReal+_HReal) < ClipY))
1149 return;
1151 // hack: allow shadow to overflow outside parent box.
1152 // In CGroupHTML context, clip is set for row
1153 if (std::abs(_ShadowX) > 0)
1155 ClipX -= 3;
1156 ClipW += 3;
1158 if (std::abs(_ShadowY) > 0)
1160 ClipY -= 3;
1161 ClipH += 3;
1164 // *** Screen Minimized?
1165 if (rVR.isMinimized())
1166 return;
1168 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
1171 // *** get current color
1172 CRGBA col, shcol;
1173 if(getModulateGlobalColor())
1175 CRGBA gcfc = CWidgetManager::getInstance()->getGlobalColorForContent();
1176 col.modulateFromColor (_Color, gcfc);
1177 shcol.modulateFromColor (_ShadowColor, gcfc);
1179 else
1181 col = _Color;
1182 shcol = _ShadowColor;
1183 col.A = (uint8)(((sint)col.A*((sint)CWidgetManager::getInstance()->getGlobalColorForContent().A+1))>>8);
1184 shcol.A = (uint8)(((sint)shcol.A*((sint)CWidgetManager::getInstance()->getGlobalColorForContent().A+1))>>8);
1187 float oow, ooh;
1188 rVR.getScreenOOSize (oow, ooh);
1190 // *** Draw multiline
1191 if ((_MultiLine)&&(_Parent != NULL))
1193 if (_Lines.empty()) return;
1195 TextContext->setHotSpot (UTextContext::BottomLeft);
1196 TextContext->setShaded (_Shadow);
1197 TextContext->setShadeOutline (_ShadowOutline);
1198 TextContext->setShadeColor (shcol);
1199 TextContext->setShadeExtent (_ShadowX*oow, _ShadowY*ooh);
1200 TextContext->setFontSize (_FontSize*_Scale);
1201 TextContext->setEmbolden (_Embolden);
1202 TextContext->setOblique (_Oblique);
1204 // Y is the base line of the string, so it must be grown up.
1205 float y = _YReal * _Scale + _FontLegHeight;
1207 if (_MultiMinLine > _Lines.size())
1209 y += getMultiMinOffsetY() * _Scale;
1212 // special selection code
1213 if(_TextSelection)
1215 sint charIndex = 0;
1216 for (sint i = 0; i<(sint)_Lines.size(); i++)
1218 CLine &currLine = *_Lines[i];
1219 for(uint k = 0; k < currLine.getNumWords(); ++k)
1221 CWord &currWord = currLine.getWord(k);
1222 charIndex += currWord.NumSpaces;
1223 sint cStart= max(charIndex, (sint)_TextSelectionStart);
1224 sint cEnd= min(charIndex+(sint)currWord.Info.StringLength, (sint)_TextSelectionEnd);
1226 // range must be valid
1227 if(cStart<cEnd)
1229 // select subset. Arg, must skip spaces because not inserted in VertexBuffer.
1230 setStringSelectionSkipingSpace(currWord.Index, currWord.Text, cStart-charIndex, cEnd-charIndex);
1232 else
1234 // select none
1235 TextContext->setStringSelection(currWord.Index, 0, 0);
1238 // next word
1239 charIndex+= (sint)currWord.Info.StringLength;
1241 charIndex += currLine.getEndSpaces() + (currLine.getLF() ? 1 : 0);
1245 // draw
1246 for (sint i = (sint)_Lines.size()-1; i >= 0; --i)
1248 CLine &currLine = *_Lines[i];
1249 // current x position
1250 float px = (float) (_XReal * _Scale + ((i==0) ? _FirstLineX : 0.f));
1252 // Center line to computed maximum line width (_WReal)
1254 // Does not give most accurate result when _WReal is much smaller than parent,
1255 // but _WReal also defines mouseover hotspot/tooltip area.
1257 // May not work correctly in CGroupParagraph (multiple text elements).
1259 if (_TextMode == Centered)
1260 px += (float)(_WReal * _Scale - (currLine.getWidth() + (i == 0 ? _FirstLineX : 0.f)) )/ 2.f;
1262 // draw each words of the line
1263 for(uint k = 0; k < currLine.getNumWords(); ++k)
1265 CWord &currWord = currLine.getWord(k);
1267 // Change the current color
1268 if(currWord.Format.Color==CRGBA::White)
1269 TextContext->setStringColor(currWord.Index, col);
1270 else
1272 CRGBA mCol;
1273 mCol.modulateFromColor(col, currWord.Format.Color);
1274 TextContext->setStringColor(currWord.Index, mCol);
1277 // skip spaces before current word
1278 float firstSpace = currWord.NumSpaces * currLine.getSpaceWidth();
1279 sint line_width = 0;
1280 if (_Underlined || _StrikeThrough)
1282 line_width = (sint)floorf(currLine.getWidthWithoutSpaces() + currLine.getSpaceWidth());
1283 line_width -= (sint)floorf(firstSpace);
1285 px += firstSpace;
1286 // skip tabulation before current word
1287 if(currWord.Format.TabX)
1288 px= max(px, (float)(_XReal * _Scale + currWord.Format.TabX*_TabWidth));
1290 // draw. We take floorf px to avoid filtering of letters that are not located on a pixel boundary
1291 float fx = px / _Scale;
1292 float fy = y / _Scale;
1293 rVR.drawText (_RenderLayer, fx, fy, currWord.Index, ClipX, ClipY, ClipX+ClipW, ClipY+ClipH, *TextContext);
1295 // Draw a line
1296 if (_Underlined)
1297 rVR.drawRotFlipBitmap (_RenderLayer, fx, fy - _FontLegHeight*0.6f / _Scale, line_width / _Scale, 1.0f / _Scale, 0, false, rVR.getBlankTextureId(), col);
1299 if (_StrikeThrough)
1300 rVR.drawRotFlipBitmap (_RenderLayer, fx, fy + _FontHeight*0.2f / _Scale, line_width / _Scale, 1.0f / _Scale, 0, false, rVR.getBlankTextureId(), col);
1302 // skip word
1303 px += currWord.Info.StringWidth;
1305 // go one line up
1306 y += (_FontHeight + _MultiLineSpace * _Scale);
1309 // reset selection
1310 if(_TextSelection)
1312 for (sint i = 0; i<(sint)_Lines.size(); i++)
1314 CLine &currLine = *_Lines[i];
1315 for(uint k = 0; k < currLine.getNumWords(); ++k)
1317 TextContext->resetStringSelection(currLine.getWord(k).Index);
1322 // *** Single Line Version (currently no support for text justification)
1323 else
1325 nlassert(_Index != 0xFFFFFFFF);
1327 TextContext->setHotSpot (UTextContext::BottomLeft);
1328 TextContext->setShaded (_Shadow);
1329 TextContext->setShadeOutline (_ShadowOutline);
1330 TextContext->setShadeColor (shcol);
1331 TextContext->setShadeExtent (_ShadowX*oow, _ShadowY*ooh);
1332 TextContext->setFontSize (_FontSize*_Scale);
1333 TextContext->setEmbolden (_Embolden);
1334 TextContext->setOblique (_Oblique);
1336 if(_LetterColors!=NULL && !TextContext->isSameLetterColors(_LetterColors, _Index))
1338 TextContext->setLetterColors(_LetterColors, _Index);
1341 // Y is the base line of the string, so it must be grown up.
1342 float y = _YReal * _Scale + _FontLegHeight;
1344 // special selection code
1345 if(_TextSelection)
1346 // select subset. Arg, must skip spaces because not inserted in VertexBuffer.
1347 setStringSelectionSkipingSpace(_Index, _Text, _TextSelectionStart, _TextSelectionEnd);
1349 // Change the current color
1350 TextContext->setStringColor(_Index, col);
1352 // draw
1353 rVR.drawText (_RenderLayer, _XReal, y / _Scale, _Index, ClipX, ClipY, ClipX+ClipW, ClipY+ClipH, *TextContext);
1355 // Draw a line
1356 if (_Underlined)
1357 rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal - _FontLegHeight*0.3f/_Scale, _WReal, 1, 0, false, rVR.getBlankTextureId(), col);
1359 if (_StrikeThrough)
1360 rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal + _FontHeight*0.2f / _Scale, _WReal, 1, 0, false, rVR.getBlankTextureId(), col);
1362 // reset selection
1363 if(_TextSelection)
1364 TextContext->resetStringSelection(_Index);
1366 // if single line clamped, may allow to draw an over
1367 if(isSingleLineTextClamped() && _OverExtendViewText && CWidgetManager::getInstance()->getPointer())
1369 // but must check first if mouse is over
1370 sint32 x = CWidgetManager::getInstance()->getPointer()->getX();
1371 sint32 y = CWidgetManager::getInstance()->getPointer()->getY();
1372 bool mouseIn;
1373 // use parent clip or self clip?
1374 if(_OverExtendViewTextUseParentRect)
1375 mouseIn= _Parent && _Parent->isIn(x,y);
1376 else
1377 mouseIn= isIn(x,y);
1378 // if the mouse cursor is in the clip area
1379 if(mouseIn)
1381 // check the window under the mouse is the root window
1382 CInterfaceGroup *pIG = CWidgetManager::getInstance()->getWindowUnder(x,y);
1383 CInterfaceElement *pParent = this;
1384 bool bFound = false;
1385 while (pParent != NULL)
1387 if (pParent == pIG)
1389 bFound = true;
1390 break;
1392 pParent = pParent->getParent();
1395 // ok => let this view text be the extend over one
1396 if(bFound)
1398 // last check: the window must not be currently moved
1399 CGroupContainerBase *gc= dynamic_cast<CGroupContainerBase*>(pIG);
1400 if(!gc || !gc->isMoving())
1402 CRGBA col= CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionViewTextOverBackColor).getValColor();
1403 CWidgetManager::getInstance()->setOverExtendViewText(this, col);
1411 // ***************************************************************************
1412 void CViewText::onAddToGroup()
1414 // Add tooltips if not done
1415 if(!_Setuped)
1416 setup();
1419 // ***************************************************************************
1420 void CViewText::setTextMode(TTextMode mode)
1422 if (mode != _TextMode)
1424 _TextMode = mode;
1425 invalidateContent ();
1429 #ifdef RYZOM_LUA_UCSTRING
1430 // ***************************************************************************
1431 void CViewText::setTextAsUtf16(const ucstring &text)
1433 setText(text.toUtf8());
1435 #endif
1437 // ***************************************************************************
1438 void CViewText::setTextLocalized(const std::string &text, bool localized)
1440 if (localized != _Localized)
1442 _Localized = localized;
1444 // Always recompute if localization and text changed
1445 setTextLocalized(text);
1446 setCase(_Text, _CaseMode);
1447 _TextLength = 0;
1448 invalidateContent();
1450 else
1452 setText(text);
1456 // ***************************************************************************
1457 void CViewText::setLocalized(bool localized)
1459 if (localized != _Localized)
1461 const std::string &text = _HardText.empty() ? _Text : _HardText;
1462 _Localized = localized;
1463 if (!text.empty() && NLMISC::startsWith(text, "ui"))
1465 setTextLocalized(text);
1466 setCase(_Text, _CaseMode);
1467 _TextLength = 0;
1468 invalidateContent();
1472 nlassert(_Text.empty() || ((_Localized && (NLMISC::startsWith(getHardText(), "ui"))) == (_HardText.empty() == _Text.empty())));
1475 // ***************************************************************************
1476 void CViewText::setTextLocalized(const std::string &text)
1478 if (_Localized && NLMISC::startsWith(text, "ui"))
1480 _HardText = text;
1481 _Text = CI18N::get(text);
1483 else
1485 _Text = text;
1486 _HardText.clear();
1490 // ***************************************************************************
1491 void CViewText::setText(const std::string &text)
1493 // common case: no special format, no case mode => easy cache test
1494 if (_FormatTags.empty() && _CaseMode == CaseNormal)
1496 if (_HardText.empty() ? text != _Text : text != _HardText)
1498 setTextLocalized(text);
1499 _TextLength = 0;
1500 // no need to call "setCase (_Text, _CaseMode);" since CaseNormal:
1501 invalidateContent();
1504 else
1506 // if the view text had some format before, no choice, must recompute all
1507 if (!_FormatTags.empty())
1509 setTextLocalized(text);
1510 setCase(_Text, _CaseMode);
1511 _TextLength = 0;
1512 invalidateContent();
1514 // else test if after the case change the cache succeed
1515 else
1517 // compute the temp cased text
1518 std::string holdText, holdHardText;
1519 holdText.swap(_Text);
1520 holdHardText.swap(_HardText);
1521 setTextLocalized(text);
1522 setCase(_Text, _CaseMode);
1523 if (holdText != _Text)
1525 _TextLength = 0;
1526 invalidateContent();
1528 else
1530 holdText.swap(_Text);
1535 nlassert(_Text.empty() || ((_Localized && (NLMISC::startsWith(text, "ui"))) == (_HardText.empty() == _Text.empty())));
1537 // clear format tags if any
1538 _FormatTags.clear();
1541 // ***************************************************************************
1542 void CViewText::setFontSizing(const std::string &chars, const std::string &fallback)
1544 _FontSizingChars = chars;
1545 _FontSizingFallback = fallback;
1548 // ***************************************************************************
1549 void CViewText::setFontName(const std::string &name)
1551 if (_FontName == name)
1552 return;
1554 if (_Index != 0xFFFFFFFF)
1555 CViewRenderer::getTextContext(_FontName)->erase (_Index);
1556 clearLines();
1558 resetTextIndex();
1560 _FontName = name;
1561 computeFontSize ();
1562 invalidateContent();
1565 // ***************************************************************************
1566 void CViewText::setFontSize (sint nFontSize, bool coef)
1568 _FontSize = nFontSize;
1569 if (coef) _FontSize += CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont).getValSInt32();
1570 _FontSizeCoef = coef;
1571 computeFontSize ();
1572 invalidateContent();
1575 // ***************************************************************************
1576 sint CViewText::getFontSize() const
1578 if (_FontSizeCoef)
1579 return _FontSize - CWidgetManager::getInstance()->getSystemOption( CWidgetManager::OptionAddCoefFont).getValSInt32();
1581 return _FontSize;
1584 // ***************************************************************************
1585 void CViewText::setEmbolden (bool embolden)
1587 _Embolden = embolden;
1588 computeFontSize ();
1589 invalidateContent();
1592 // ***************************************************************************
1593 void CViewText::setOblique (bool oblique)
1595 _Oblique = oblique;
1596 computeFontSize ();
1597 invalidateContent();
1600 // ***************************************************************************
1601 void CViewText::setColor(const NLMISC::CRGBA & color)
1603 _Color = color;
1606 // ***************************************************************************
1607 void CViewText::setShadow (bool bShadow)
1609 _Shadow = bShadow;
1610 computeFontSize ();
1611 invalidateContent();
1614 // ***************************************************************************
1615 void CViewText::setShadowOutline (bool bShadowOutline)
1617 _ShadowOutline = bShadowOutline;
1618 computeFontSize ();
1619 invalidateContent();
1622 // ***************************************************************************
1623 void CViewText::setShadowColor(const NLMISC::CRGBA & color)
1625 _ShadowColor = color;
1628 // ***************************************************************************
1629 void CViewText::setShadowOffset(sint32 x, sint32 y)
1631 _ShadowX = x;
1632 _ShadowY = y;
1635 // ***************************************************************************
1636 void CViewText::setLineMaxW (sint nMaxW, bool invalidate)
1638 if(_LineMaxW!=nMaxW)
1640 _LineMaxW = nMaxW;
1641 if (invalidate)
1642 invalidateContent();
1646 // ***************************************************************************
1647 int CViewText::luaSetLineMaxW(CLuaState &ls)
1649 CLuaIHM::checkArgCount(ls, "setLineMaxW", 1);
1650 sint32 value;
1651 if(CLuaIHM::popSINT32(ls, value))
1653 setLineMaxW(ceilf(value / _Scale));
1655 return 0;
1658 // ***************************************************************************
1659 void CViewText::setMultiLine (bool bMultiLine)
1661 _MultiLine = bMultiLine;
1662 invalidateContent();
1665 // ***************************************************************************
1666 void CViewText::setMultiLineSpace (sint nMultiLineSpace)
1668 _MultiLineSpace = nMultiLineSpace;
1669 invalidateContent();
1672 // ***************************************************************************
1673 void CViewText::setMultiLineMaxWOnly (bool state)
1675 _MultiLineMaxWOnly = state;
1676 invalidateContent();
1679 // ***************************************************************************
1680 void CViewText::setMultiLineClipEndSpace (bool state)
1682 _MultiLineClipEndSpace= state;
1683 invalidateContent();
1686 // ***************************************************************************
1687 uint CViewText::getFontWidth() const
1689 return _FontWidth / _Scale;
1692 // ***************************************************************************
1693 uint CViewText::getFontHeight() const
1695 return _FontHeight / _Scale;
1698 // ***************************************************************************
1699 uint CViewText::getFontLegHeight() const
1701 return _FontLegHeight / _Scale;
1704 // ***************************************************************************
1705 float CViewText::getLineHeight() const
1707 return _FontHeight / _Scale + _MultiLineSpace;
1710 // ***************************************************************************
1711 uint CViewText::getMultiMinOffsetY() const
1713 uint dy = 0;
1714 if (_MultiMinLine > _Lines.size())
1716 // first line is always present even if _Lines is empty
1717 uint nbLines = _MultiMinLine - std::max((sint)1, (sint)_Lines.size());
1718 dy = (nbLines * _FontHeight + (nbLines - 1) * _MultiLineSpace * _Scale) / _Scale;
1720 return dy;
1723 // ***************************************************************************
1724 void CViewText::flushWordInLine(std::string &ucCurrentWord, bool &linePushed, const CFormatInfo &wordFormat)
1726 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
1727 // create a new line?
1728 if(!linePushed)
1730 _Lines.push_back(TLineSPtr(new CLine));
1731 linePushed= true;
1733 // Append to the last line
1734 _Lines.back()->addWord(ucCurrentWord, 0, wordFormat, _FontWidth, _TabWidth, *TextContext);
1735 // reset the word
1736 ucCurrentWord.clear();
1740 // ***************************************************************************
1741 void CViewText::updateTextContextMultiLine(float nMaxWidth)
1743 // ucchar ucLetter;
1744 UTextContext::CStringInfo si;
1745 uint i = 0;
1746 // word state
1747 std::string ucCurrentWord;
1748 CFormatInfo wordFormat;
1749 // line state
1750 float rWidthCurrentLine = 0;
1751 bool linePushed= false;
1752 // for all the text
1753 // uint textSize= (uint)_Text.size();
1754 uint formatTagIndex= 0;
1755 nMaxWidth *= _Scale;
1756 //for (i = 0; i < textSize; ++i)
1757 CUtfStringView sv(_Text);
1758 ::u32string ucStrLetter(1, ' ');
1759 for (CUtfStringView::iterator it(sv.begin()), end(sv.end()); it != end; ++it, ++i)
1761 if(isFormatTagChange(i, formatTagIndex))
1763 // If the word was not empty before this color tag
1764 if(!ucCurrentWord.empty())
1765 flushWordInLine(ucCurrentWord, linePushed, wordFormat);
1767 // get new color and skip ctIndex.
1768 getFormatTagChange(i, formatTagIndex, wordFormat);
1770 // Ensure the line witdh count the tab
1771 rWidthCurrentLine= max(rWidthCurrentLine, (float)wordFormat.TabX*_TabWidth);
1774 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
1776 // Parse the letter
1778 if (*it == '\n')
1780 flushWordInLine(ucCurrentWord, linePushed, wordFormat);
1781 // reset line state
1782 linePushed= false;
1783 rWidthCurrentLine = 0;
1785 else
1787 ucStrLetter[0] = *it;
1788 si = TextContext->getStringInfo (ucStrLetter);
1789 if ((rWidthCurrentLine + si.StringWidth) > nMaxWidth)
1791 flushWordInLine(ucCurrentWord, linePushed, wordFormat);
1793 // reset line state, and begin with the cut letter
1794 linePushed= false;
1795 rWidthCurrentLine = si.StringWidth;
1796 ucCurrentWord.clear();
1797 CUtfStringView::append(ucCurrentWord, *it);
1799 else
1801 // Grow the current word
1802 CUtfStringView::append(ucCurrentWord, *it);
1803 rWidthCurrentLine += si.StringWidth;
1808 if (ucCurrentWord.length())
1810 flushWordInLine(ucCurrentWord, linePushed, wordFormat);
1815 // ***************************************************************************
1816 void CViewText::addDontClipWordLine(std::vector<CWord> &currLine)
1818 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
1819 // create a new line
1820 _Lines.push_back(TLineSPtr(new CLine));
1822 // Fill it with words. if all words of same color, create only one CWord
1823 if (!currLine.empty())
1825 CFormatInfo lineWordFormat= currLine[0].Format;
1826 std::string lineWord;
1827 for(uint i=0;i<currLine.size();i++)
1829 // If different from last, flush
1830 if(currLine[i].Format!=lineWordFormat)
1832 // add the current lineWord to the line.
1833 _Lines.back()->addWord(lineWord, 0, lineWordFormat, _FontWidth, _TabWidth, *TextContext);
1834 // get new lineWordFormat
1835 lineWordFormat= currLine[i].Format;
1836 // and clear
1837 lineWord.clear();
1840 // Append the word with space to the lineWord.
1841 std::string blank(currLine[i].NumSpaces, ' ');
1842 lineWord += blank;
1843 lineWord += currLine[i].Text;
1846 if(!lineWord.empty())
1847 _Lines.back()->addWord(lineWord, 0, lineWordFormat, _FontWidth, _TabWidth, *TextContext);
1849 // clear
1850 currLine.clear();
1854 // ***************************************************************************
1855 void CViewText::updateTextContextMultiLineJustified(float nMaxWidth, bool expandSpaces)
1857 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
1858 UTextContext::CStringInfo si;
1860 TCharPos currPos = 0;
1862 // precLineWidth valid only id precedent line is part of same paragraph.
1863 float precLineWidth= 0;
1864 float lineWidth = (float)_FirstLineX; // width of the current line
1865 uint numWordsInLine = 0; // number of words in the current line
1866 bool isParagraphStart = true; // A paragraph is a group of characters between 2 \n
1867 bool lineFeed;
1868 bool breakLine;
1870 vector<CWord> currLine; // if spaces are not expanded, all words of a line are inserted here (NB: index and stringInfo not filled)
1871 std::string wordValue;
1872 CFormatInfo wordFormat;
1873 uint formatTagIndex= 0;
1875 nMaxWidth *= _Scale;
1876 while (currPos != _Text.length())
1878 TCharPos spaceEnd;
1879 TCharPos wordEnd;
1880 uint numSpaces;
1881 float newLineWidth = 0;
1882 breakLine = false;
1884 if (_Text[currPos] == '\n')
1886 lineFeed = true;
1888 else
1890 lineFeed = false;
1891 // Skip spaces and count them
1892 spaceEnd = _Text.find_first_not_of(" ", currPos);
1893 if (spaceEnd == std::string::npos)
1895 spaceEnd = _Text.length();
1897 numSpaces = (uint) (spaceEnd - currPos);
1898 if (!isParagraphStart && numSpaces != 0 && numWordsInLine == 0) // Are these the first spaces of the line ?
1900 if (!_Lines.empty())
1902 /* Yoyo: I changed this (added the "cut space"), because in editBox, it is so strange when
1903 the word hit the end of line, and if you add spaces just after, nothing happens because
1904 cursor pos is clamped at end of line.
1906 // Cannot put all of thoses spaces to the prec end of line?
1907 if(_MultiLineClipEndSpace && precLineWidth + numSpaces * _SpaceWidth > nMaxWidth)
1909 // put some of these spaces at the end of the previous line.
1910 sint maxNumSpaces= (sint)floorf((nMaxWidth - precLineWidth) / _SpaceWidth);
1911 _Lines.back()->setEndSpaces(maxNumSpaces);
1912 // And start the new lines with the remaining spaces.
1913 numSpaces-= maxNumSpaces;
1914 currPos+= maxNumSpaces;
1916 else
1918 // ok, put all spaces to previous line
1919 _Lines.back()->setEndSpaces(numSpaces);
1920 currPos = spaceEnd;
1921 numSpaces= 0;
1923 if(currPos >=_Text.length())
1924 break;
1928 // Detect change of wordFormat at the beginning of the word
1929 if(isFormatTagChange((uint)spaceEnd, formatTagIndex))
1931 getFormatTagChange((uint)spaceEnd, formatTagIndex, wordFormat);
1934 // Get word until a space, a \n, or a FormatTagChange is encountered
1935 uint i;
1936 for(i= (uint)spaceEnd;i<(uint)_Text.length();i++)
1938 char c= _Text[i];
1939 if(c==' ' || c=='\n')
1940 break;
1941 // If change of color tag, stop the word, but don't take the new color now.
1942 if(isFormatTagChange(i, formatTagIndex))
1943 break;
1945 wordEnd = i;
1948 // Get the word value.
1949 wordValue = _Text.substr(spaceEnd, wordEnd - spaceEnd);
1950 // compute width of word
1951 si = TextContext->getStringInfo(wordValue);
1953 // compute size of spaces/Tab + word
1954 newLineWidth = lineWidth + numSpaces * _SpaceWidth;
1955 newLineWidth = max(newLineWidth, (float)wordFormat.TabX*_TabWidth);
1956 newLineWidth+= si.StringWidth;
1959 // Does the word go beyond the end of line ?
1960 if (!lineFeed && newLineWidth > nMaxWidth)
1962 // Have we enough room for this word on a line ?
1963 bool roomForThisWord = (numWordsInLine > 0) || ( (newLineWidth - lineWidth) < nMaxWidth );
1965 // not enough room for that word
1966 // If it is the only word of the line, just split it
1967 // Otherwise, scale the spaces between words so that the line as the maximum width
1968 if (roomForThisWord)
1970 if (expandSpaces)
1972 nlassert(!_Lines.empty());
1973 nlassert(_Lines.back()->getNumWords() > 0);
1975 // Yoyo: if the line has tab, then don't justify
1976 if(wordFormat.TabX > 0)
1977 _Lines.back()->setSpaceWidth(_SpaceWidth);
1978 else
1980 // Scale the width so that the line has the maximum width
1981 float roomForSpaces = nMaxWidth - _Lines.back()->getWidthWithoutSpaces();
1982 uint startNumSpaces = _Lines.back()->getNumSpaces();
1983 if (startNumSpaces != 0)
1985 _Lines.back()->setSpaceWidth(roomForSpaces / startNumSpaces);
1987 else
1989 _Lines.back()->setSpaceWidth(_SpaceWidth);
1993 else
1995 breakLine = true;
1997 // we dont change the position in the input string so that the current will be processed on the next line
2000 else // it is the only word on the line..
2002 // .. so split it
2003 // 1) Check if spaces go beyond the end of line
2004 if (numSpaces * _SpaceWidth > nMaxWidth)
2006 uint maxNumSpaces = std::max(1U, (uint) (nMaxWidth / _SpaceWidth));
2007 CWord spaceWord; // a word with only spaces in it
2008 spaceWord.build ("", *TextContext, maxNumSpaces);
2009 spaceWord.Format= wordFormat;
2010 _Lines.push_back(TLineSPtr(new CLine));
2011 _Lines.back()->addWord(spaceWord, _FontWidth, _TabWidth);
2012 if (expandSpaces)
2014 _Lines.back()->setSpaceWidth(nMaxWidth / (float) maxNumSpaces);
2016 else
2018 _Lines.back()->setSpaceWidth(_SpaceWidth);
2020 currPos = currPos + maxNumSpaces;
2022 else
2024 float px = numSpaces * _SpaceWidth;
2025 ::u32string oneChar(1, ' ');
2026 CUtfStringView wsv(wordValue);
2027 CUtfStringView::iterator wit(wsv.begin()), wend(wsv.end());
2028 for (; wit != wend; ++wit)
2030 oneChar[0] = *wit;
2031 si = TextContext->getStringInfo(oneChar);
2032 if ((uint) (px + si.StringWidth) > nMaxWidth) break;
2033 px += si.StringWidth;
2035 ptrdiff_t currChar = std::max((ptrdiff_t)1, (ptrdiff_t)wit.ptr() - (ptrdiff_t)wsv.ptr()); // must fit at least one character otherwise there's an infinite loop
2036 wordValue = _Text.substr(spaceEnd, currChar);
2037 CWord word;
2038 word.build(wordValue, *TextContext, numSpaces);
2039 word.Format= wordFormat;
2040 _Lines.push_back(TLineSPtr(new CLine));
2041 float roomForSpaces = nMaxWidth - word.Info.StringWidth;
2042 if (expandSpaces && numSpaces != 0)
2044 _Lines.back()->setSpaceWidth(roomForSpaces / (float) numSpaces);
2046 else
2048 _Lines.back()->setSpaceWidth(0);
2050 _Lines.back()->addWord(word, _FontWidth, _TabWidth);
2051 currPos = currPos + numSpaces + currChar;
2054 // reset line
2055 numWordsInLine = 0;
2056 precLineWidth= lineWidth;
2057 lineWidth = 0;
2058 isParagraphStart = false;
2060 else if (!lineFeed) // the end of line hasn't been reached
2062 if (expandSpaces)
2064 // add in the current line (and create one if necessary)
2065 if (numWordsInLine == 0)
2067 _Lines.push_back(TLineSPtr(new CLine));
2068 _Lines.back()->setSpaceWidth(_SpaceWidth);
2070 if (!wordValue.empty() || numSpaces != 0)
2072 CWord word;
2073 word.build(wordValue, *TextContext, numSpaces);
2074 word.Format= wordFormat;
2075 // update line width
2076 _Lines.back()->addWord(word, _FontWidth, _TabWidth);
2077 ++numWordsInLine;
2080 else
2082 CWord word;
2083 // Don't build here, this is used as temp data.
2084 word.Text= wordValue;
2085 word.NumSpaces= numSpaces;
2086 word.Format= wordFormat;
2087 // Append to the temp Data.
2088 currLine.push_back(word);
2090 ++numWordsInLine;
2092 lineWidth = newLineWidth;
2093 currPos = wordEnd;
2095 else
2097 // '\n' was encountered
2098 ++ currPos;
2099 isParagraphStart = true;
2101 if (lineFeed || breakLine) // '\n' was encoutered, or a linefeed has been asked
2103 // !expandSpaces => insert minimum words according to word color.
2104 if (!expandSpaces)
2106 // Add the new line.
2107 addDontClipWordLine(currLine);
2108 // LineFeed?
2109 if (lineFeed)
2111 _Lines.back()->setLF(true);
2114 // expandSpaces => just add a empty line.
2115 else
2117 if (numWordsInLine == 0)
2119 // if nothing has been inserted in this line, create at least an empty line
2120 _Lines.push_back(TLineSPtr(new CLine));
2122 if (lineFeed)
2124 _Lines.back()->setLF(true);
2127 lineWidth = 0.f;
2128 numWordsInLine = 0;
2132 // if current line hasn't been pushed, add it
2133 if (!expandSpaces && !currLine.empty())
2135 // Add new line
2136 addDontClipWordLine(currLine);
2139 // if the text ends with \n, must insert the last line ourself
2140 if (!_Text.empty() && _Text[_Text.length() - 1] == '\n')
2142 _Lines.push_back(TLineSPtr(new CLine));
2147 // ***************************************************************************
2148 void CViewText::updateTextContext ()
2150 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
2152 TextContext->setHotSpot (UTextContext::BottomLeft);
2153 TextContext->setShaded (_Shadow);
2154 TextContext->setShadeOutline (_ShadowOutline);
2155 TextContext->setFontSize (_FontSize*_Scale);
2156 TextContext->setEmbolden (_Embolden);
2157 TextContext->setOblique (_Oblique);
2159 // default state
2160 _SingleLineTextClamped= false;
2162 if ((_MultiLine)&&(_Parent != NULL))
2164 float nMaxWidth = getCurrentMultiLineMaxW();
2165 _LastMultiLineMaxW = nMaxWidth;
2166 clearLines();
2167 if (nMaxWidth <= 0)
2169 // parent size may not be known yet
2170 return;
2172 switch(_TextMode)
2174 case ClipWord: updateTextContextMultiLine(nMaxWidth); break;
2175 case Centered: // fallthru to DontClipWord
2176 case DontClipWord: updateTextContextMultiLineJustified(nMaxWidth, false); break;
2177 case Justified: updateTextContextMultiLineJustified(nMaxWidth, true); break;
2180 // Special case for multiline limited in number of lines
2181 if (!_Lines.empty() && (_MultiMaxLine > 0) && (_Lines.size() > _MultiMaxLine))
2183 while (_Lines.size() > _MultiMaxLine)
2185 _Lines.back()->clear(*TextContext);
2186 _Lines.pop_back();
2188 if (_OverflowText.size() > 0)
2190 _Lines.pop_back();
2191 CViewText::CLine *endLine = new CViewText::CLine;
2192 CViewText::CWord w;
2193 w.build(_OverflowText, *TextContext);
2194 endLine->addWord(w, _FontWidth, _TabWidth);
2195 _Lines.push_back(TLineSPtr(endLine));
2199 // Calculate size
2200 float rMultiLineSpace = _MultiLineSpace * _Scale;
2201 float rTotalW = 0;
2202 for (uint i = 0; i < _Lines.size(); ++i)
2204 rTotalW = std::max(_Lines[i]->getWidth() + ((i==0)?_FirstLineX:0), rTotalW);
2206 _W = (sint)ceilf(rTotalW / _Scale);
2207 _H = std::max(_FontHeight / _Scale, ceilf((_FontHeight * _Lines.size() + std::max(0, sint(_Lines.size()) - 1) * rMultiLineSpace) / _Scale));
2209 // See if we should pretend to have at least X lines
2210 if (_MultiMinLine > 1)
2211 _H = std::max(_H, sint((_FontHeight * _MultiMinLine + (_MultiMinLine - 1) * rMultiLineSpace)/_Scale));
2213 // Compute tooltips size
2214 if (!_Tooltips.empty())
2215 for (uint i=0 ; i<_Lines.size() ; ++i)
2217 for (uint j=0 ; j<_Lines[i]->getNumWords() ; ++j)
2219 CWord word = _Lines[i]->getWord(j);
2221 if (word.Format.IndexTt != -1)
2223 if (_Tooltips.size() > (uint)word.Format.IndexTt)
2225 CCtrlToolTip *pTooltip = _Tooltips[word.Format.IndexTt];
2227 sint y = (sint) ceilf(((_FontHeight + rMultiLineSpace) * (_Lines.size() - i - 1))/_Scale);
2229 pTooltip->setX(0);
2230 pTooltip->setY(y);
2231 pTooltip->setW(getCurrentMultiLineMaxW());
2232 pTooltip->setH(_FontHeight);
2238 else // Single line code
2240 if (_Index != 0xFFFFFFFF)
2241 TextContext->erase (_Index);
2243 // Common case: no W clamp
2244 _Index = TextContext->textPush (_Text);
2245 _Info = TextContext->getStringInfo (_Index);
2246 _W = (sint)ceilf(_Info.StringWidth / _Scale);
2248 // Rare case: clamp W => recompute slowly, cut letters
2249 if(_W > _LineMaxW)
2251 TextContext->erase (_Index);
2253 // char ucLetter;
2254 UTextContext::CStringInfo si;
2255 std::string ucCurrentLine;
2256 ucCurrentLine.reserve(_Text.size());
2258 // Append ... to the end of line
2259 float dotWidth = 0.f;
2260 if (_OverflowText.size() > 0)
2262 si = TextContext->getStringInfo(_OverflowText);
2263 dotWidth = si.StringWidth;
2266 // scale LineMaxW to actual font size
2267 float fLineMaxW = (float)_LineMaxW * _Scale;
2268 float rWidthCurrentLine = 0;
2269 // for all the text
2270 if (_ClampRight)
2272 CUtfStringView sv(_Text);
2273 ::u32string ucStrLetter = ::u32string(1, (u32char)' ');
2274 for (CUtfStringView::iterator it(sv.begin()), end(sv.end()); it != end; ++it)
2276 ucStrLetter[0] = *it;
2277 si = TextContext->getStringInfo (ucStrLetter);
2278 if ((rWidthCurrentLine + si.StringWidth + dotWidth) > fLineMaxW)
2280 break;
2282 else
2284 // Grow the current line
2285 CUtfStringView::append(ucCurrentLine, *it);
2286 rWidthCurrentLine += si.StringWidth;
2290 // Add the dots
2291 if (_OverflowText.size() > 0)
2293 ucCurrentLine += _OverflowText;
2296 else
2298 // FIXME: Optimize reverse UTF iteration
2299 ::u32string uctext = CUtfStringView(_Text).toUtf32();
2300 ::u32string ucStrLetter = ::u32string(1, (u32char)' ');
2301 for (sint i = (sint)uctext.size() - 1; i >= 0; --i)
2303 ucStrLetter[0] = uctext[i];
2304 si = TextContext->getStringInfo (ucStrLetter);
2305 if ((rWidthCurrentLine + si.StringWidth + dotWidth) > fLineMaxW)
2307 break;
2309 else
2311 // Grow the current line
2312 std::string tmp;
2313 CUtfStringView::append(tmp, uctext[i]);
2314 ucCurrentLine = tmp + ucCurrentLine;
2315 rWidthCurrentLine += si.StringWidth;
2319 // Add the dots
2320 if (_OverflowText.size() > 0)
2322 ucCurrentLine = _OverflowText + ucCurrentLine;
2326 // And so setup this trunc text
2327 _Index = TextContext->textPush (ucCurrentLine);
2328 _Info = TextContext->getStringInfo (_Index);
2329 _W = (sint)ceilf(_Info.StringWidth / _Scale);
2331 _SingleLineTextClamped= true;
2334 // same height always
2335 _H = (sint)ceilf(_FontHeight / _Scale);
2338 _InvalidTextContext= false;
2341 // ***************************************************************************
2342 void CViewText::updateCoords()
2344 if (_AutoClamp)
2346 CViewBase::updateCoords ();
2348 // If there's no parent, try the parent of the parent element.
2349 // Since we will be under the same group
2350 CInterfaceGroup *parent = _Parent;
2351 if( parent == NULL )
2353 if( _ParentElm != NULL )
2354 parent = _ParentElm->getParent();
2357 if (parent)
2359 // avoid resizing parents to compute the limiter
2360 while (parent && (parent->getResizeFromChildW() || parent->isGroupList() ))
2362 // NB nico : the dynamic_cast for CGroupList is bad!!
2363 // can't avoid it for now, because, CGroupList implicitly does a "resize from child" in its update coords
2364 // ...
2365 parent = parent->getParent();
2367 if (parent)
2369 if (_ClampRight)
2371 sint32 parentRight = parent->getXReal() + parent->getWReal() - (sint32) _AutoClampOffset;
2372 setLineMaxW(std::max((sint32) 0, parentRight - _XReal));
2374 else
2376 sint32 parentLeft = parent->getXReal() + (sint32) _AutoClampOffset;
2377 setLineMaxW(std::max((sint32) 0, _XReal + _WReal - parentLeft));
2383 if(_InvalidTextContext)
2384 updateTextContext();
2386 CViewBase::updateCoords ();
2389 // ***************************************************************************
2390 sint CViewText::getLineFromIndex(uint index, bool cursorDisplayedAtEndOfPreviousLine /* = true*/) const
2392 if (index > _Text.length()) return -1;
2393 if (_MultiLine)
2395 uint charIndex = 0;
2396 for(sint i = 0; i < (sint) _Lines.size(); ++i)
2398 CLine &currLine = *_Lines[i];
2399 uint newCharIndex = charIndex + currLine.getNumChars() + currLine.getEndSpaces() + (currLine.getLF() ? 1 : 0);
2400 if (newCharIndex > index)
2402 if (i != 0 && cursorDisplayedAtEndOfPreviousLine && charIndex == index)
2404 return i - 1;
2406 else
2408 return i;
2411 charIndex = newCharIndex;
2413 // return (sint)_Lines.size() - 1;
2414 return -1;
2416 else
2418 return 0;
2422 // ***************************************************************************
2423 sint CViewText::getLineStartIndex(uint line) const
2425 uint charIndex = 0;
2426 if (line >= _Lines.size()) return -1;
2427 for(uint i = 0; i < line; ++i)
2429 CLine &currLine = *_Lines[i];
2430 charIndex += currLine.getNumChars() + currLine.getEndSpaces() + (currLine.getLF() ? 1 : 0);
2432 // skip all spaces at start of line (unless there are only spaces in the line)
2433 std::string::size_type nextPos = _Text.find_first_not_of(' ', charIndex);
2434 if (nextPos != std::string::npos)
2436 if (getLineFromIndex(charIndex) == (sint) line)
2438 return (sint) nextPos;
2441 return charIndex;
2444 // ***************************************************************************
2445 void CViewText::getLineEndIndex(uint line, sint &index, bool &endOfPreviousLine) const
2447 sint startIndex = getLineStartIndex(line);
2448 if (startIndex == -1)
2450 index = -1;
2451 endOfPreviousLine = false;
2452 return;
2454 index = startIndex + _Lines[line]->getNumChars() + _Lines[line]->getEndSpaces();
2455 endOfPreviousLine = !_Lines[line]->getLF();
2458 #ifdef RYZOM_LUA_UCSTRING
2459 // ***************************************************************************
2460 void CViewText::setHardTextAsUtf16(const ucstring &ht)
2462 setHardText(ht.toUtf8());
2464 #endif
2466 // ***************************************************************************
2467 void CViewText::setHardText (const std::string &ht)
2469 if (!_Localized)
2471 setText(std::string());
2472 _Localized = true;
2474 setText(ht);
2477 #ifdef RYZOM_LUA_UCSTRING
2478 // ***************************************************************************
2479 ucstring CViewText::getTextAsUtf16() const
2481 return CUtfStringView(getText()).toUtf16();
2484 // ***************************************************************************
2485 ucstring CViewText::getHardTextAsUtf16() const
2487 return CUtfStringView(getHardText()).toUtf16();
2489 #endif
2491 // ***************************************************************************
2492 string CViewText::getColorAsString() const
2494 return NLMISC::toString(_Color.R) + " " + NLMISC::toString(_Color.G) + " " + NLMISC::toString(_Color.B) + " " + NLMISC::toString(_Color.A);
2497 // ***************************************************************************
2498 void CViewText::setColorAsString(const string &ht)
2500 _Color = convertColor (ht.c_str());
2503 // ***************************************************************************
2504 NLMISC::CRGBA CViewText::getColorRGBA() const
2506 return _Color;
2509 // ***************************************************************************
2510 void CViewText::setColorRGBA(NLMISC::CRGBA col)
2512 _Color = col;
2515 // ***************************************************************************
2516 void CViewText::getCharacterPositionFromIndex(sint index, bool cursorAtPreviousLineEnd, float &x, float &y, float &height) const
2518 if (!_TextLength && _Text.size())
2519 _TextLength = CUtfStringView(_Text).count();
2520 NLMISC::clamp(index, 0, (sint)_TextLength);
2521 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
2522 TextContext->setHotSpot (UTextContext::BottomLeft);
2523 TextContext->setShaded (_Shadow);
2524 TextContext->setShadeOutline (_ShadowOutline);
2525 TextContext->setFontSize (_FontSize*_Scale);
2526 TextContext->setEmbolden (_Embolden);
2527 TextContext->setOblique (_Oblique);
2528 height = getLineHeight();
2530 if (_MultiLine)
2532 float fx, fy;
2533 float dy = getMultiMinOffsetY() * _Scale;
2534 float nMaxWidth = getCurrentMultiLineMaxW() * _Scale;
2536 uint charIndex = 0;
2537 // special case for end of text
2538 if (index >= (sint)_TextLength)
2540 fy = dy;
2541 if (_Lines.empty())
2543 x = 0;
2544 fx = 0;
2546 else
2548 CLine &lastLine = *_Lines.back();
2549 fx = lastLine.getWidth() + lastLine.getEndSpaces() * lastLine.getSpaceWidth();
2550 fx = std::min(fx, nMaxWidth);
2553 x = fx / _Scale;
2554 y = fy / _Scale;
2555 return;
2557 for(sint i = 0; i < (sint) _Lines.size(); ++i)
2559 if (i != 0 && charIndex == (uint) index && cursorAtPreviousLineEnd)
2561 // should display the character at the end of previous line
2562 CLine &currLine = *_Lines[i - 1];
2563 fy = (_FontHeight + _MultiLineSpace * _Scale) * (_Lines.size() - i) + dy;
2564 fx = currLine.getWidth() + currLine.getEndSpaces() * currLine.getSpaceWidth();
2565 fx = std::min(fx, nMaxWidth);
2567 x = fx / _Scale;
2568 y = fy / _Scale;
2569 return;
2571 CLine &currLine = *_Lines[i];
2572 uint newCharIndex = charIndex + currLine.getNumChars() + currLine.getEndSpaces() + (_Lines[i]->getLF() ? 1 : 0);
2573 if ((sint) newCharIndex > index)
2575 // ok, this line contains the character, now, see which word contains it.
2576 fy = (_FontHeight + _MultiLineSpace * _Scale) * (_Lines.size() - 1 - i) + dy;
2577 // see if the index is in the spaces at the end of line
2578 if (index - charIndex >= currLine.getNumChars())
2580 uint numSpaces = index - charIndex - currLine.getNumChars();
2581 fx = currLine.getWidth() + numSpaces * _SpaceWidth;
2582 fx = std::min(fx, nMaxWidth);
2584 x = fx / _Scale;
2585 y = fy / _Scale;
2586 return;
2588 // now, search containing word in current line
2589 float px = (float)_FirstLineX;
2590 for(uint k = 0; k < currLine.getNumWords(); ++k)
2592 CWord &currWord = currLine.getWord(k);
2593 if ((sint) (charIndex + currWord.NumSpaces + currWord.Info.StringLength) >= index)
2595 // character is in currWord or the in spaces preceding it
2596 // check if the character is in the word
2597 if ((uint) (index - charIndex) > currWord.NumSpaces)
2599 // get the x position
2600 // compute the size
2601 UTextContext::CStringInfo si = TextContext->getStringInfo(currWord.Text, (ptrdiff_t)index - charIndex - currWord.NumSpaces);
2602 fx = px + si.StringWidth + currWord.NumSpaces * currLine.getSpaceWidth();
2604 x = fx / _Scale;
2605 y = fy / _Scale;
2606 return;
2608 else
2610 // character is in the spaces preceding the word
2611 fx = px + currLine.getSpaceWidth() * (index - charIndex);
2613 x = fx / _Scale;
2614 y = fy / _Scale;
2615 return;
2618 charIndex += (uint)currWord.Info.StringLength + currWord.NumSpaces;
2619 px += currWord.NumSpaces * currLine.getSpaceWidth() + currWord.Info.StringWidth;
2622 charIndex = newCharIndex;
2626 else
2628 // get the x position
2629 // compute the size
2630 UTextContext::CStringInfo si = TextContext->getStringInfo(_Text, index);
2631 y = 0;
2632 x = (sint) ceilf(si.StringWidth / _Scale);
2636 // ***************************************************************************
2637 // Tool fct : From a word and a x coordinate (font scale), give the matching character index
2638 static uint getCharacterIndex(const std::string &textValue, float x, NL3D::UTextContext &textContext)
2640 float px = 0.f;
2642 UTextContext::CStringInfo si;
2643 ::u32string singleChar(1, ' ');
2644 uint i = 0;
2645 NLMISC::CUtfStringView sv(textValue);
2646 for (NLMISC::CUtfStringView::iterator it(sv.begin()), end(sv.end()); it != end; ++it, ++i)
2648 // get character width
2649 singleChar[0] = *it;
2650 si = textContext.getStringInfo(singleChar);
2651 px += si.StringWidth;
2652 // the character is at the i - 1 position
2653 if (px > x)
2655 // if the half of the character is after the cursor, then prefer select the next one (like in Word)
2656 if(px-si.StringWidth/2.f < x)
2657 i++;
2658 break;
2661 return i;
2664 // ***************************************************************************
2665 void CViewText::getCharacterIndexFromPosition(float x, float y, uint &index, bool &cursorAtPreviousLineEnd) const
2667 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
2669 if (!_TextLength && _Text.size())
2670 _TextLength = CUtfStringView(_Text).count();
2672 x *= _Scale;
2673 y = roundf(y * _Scale);
2675 // setup the text context
2676 TextContext->setHotSpot (UTextContext::BottomLeft);
2677 TextContext->setShaded (_Shadow);
2678 TextContext->setShadeOutline (_ShadowOutline);
2679 TextContext->setFontSize (_FontSize*_Scale);
2680 TextContext->setEmbolden (_Embolden);
2681 TextContext->setOblique (_Oblique);
2682 // find the line where the character is
2683 // CViewRenderer &rVR = *CViewRenderer::getInstance();
2684 uint charPos = 0;
2685 if (_MultiLine)
2687 y -= getMultiMinOffsetY() * _Scale;
2688 // seek the line
2689 float py = 0.f;
2690 if (py > y)
2692 index = (uint)_TextLength;
2693 cursorAtPreviousLineEnd = false;
2694 return;
2696 sint line;
2697 for (line = (uint)_Lines.size() - 1; line >= 0; --line)
2699 float newPy = py + _FontHeight + _MultiLineSpace * _Scale;
2700 if (newPy > y)
2702 break;
2704 py = newPy;
2706 if (line == -1)
2708 index = 0;
2709 cursorAtPreviousLineEnd = false;
2710 return; // above the first line, so take character 0
2712 // compute character index at start of line
2713 sint i;
2714 for (i = 0; i < line; ++i)
2716 charPos += _Lines[i]->getNumChars() + _Lines[i]->getEndSpaces() + (_Lines[i]->getLF() ? 1 : 0);
2718 // seek word that contains the character
2719 CLine &currLine = *_Lines[line];
2720 // See if character is in the ending spaces
2721 if (x >= (sint) currLine.getWidth())
2723 // Add _SpaceWidth/2 to select between chars
2724 sint numSpaces = _SpaceWidth != 0 ? (sint) (((float) x + _SpaceWidth/2 - currLine.getWidth()) / _SpaceWidth)
2725 : 0;
2726 clamp(numSpaces, 0, (sint)currLine.getEndSpaces());
2727 index = charPos + currLine.getNumChars() + numSpaces;
2728 cursorAtPreviousLineEnd = !_Lines[i]->getLF();
2729 return;
2732 cursorAtPreviousLineEnd = false;
2733 float px = (float)_FirstLineX;
2734 for(uint k = 0; k < currLine.getNumWords(); ++k)
2736 CWord &currWord = currLine.getWord(k);
2737 float spacesWidth = currLine.getSpaceWidth() * currWord.NumSpaces;
2738 float newPx = px + currWord.Info.StringWidth + spacesWidth;
2739 if (newPx >= x) // if the word contains the x position..
2741 if (x < (px + spacesWidth))
2743 // the coords x is in the spaces that are preceding the word
2744 // Add spaceWidth/2 to select between chars
2745 sint numSpaces = currLine.getSpaceWidth() != 0 ? (sint) ((x + currLine.getSpaceWidth()/2 - px) / currLine.getSpaceWidth())
2746 : 0;
2747 clamp(numSpaces, 0, (sint)currWord.NumSpaces);
2748 index = numSpaces + charPos;
2749 return;
2751 else
2753 // the coord is in the word itself
2754 index = charPos + currWord.NumSpaces + getCharacterIndex(currWord.Text, x - (px + spacesWidth), *TextContext);
2755 return;
2758 px = newPx;
2759 charPos += (uint)currWord.Info.StringLength + currWord.NumSpaces;
2761 index = charPos;
2762 return;
2764 else
2766 cursorAtPreviousLineEnd = false;
2767 if (y < 0)
2769 index = (uint)_TextLength;
2770 return;
2772 if (y > (sint) _FontHeight)
2774 index = 0;
2775 return;
2777 index = getCharacterIndex(_Text, x, *TextContext);
2778 return;
2782 // ***************************************************************************
2783 void CViewText::enableStringSelection(uint start, uint end)
2785 _TextSelection= true;
2786 _TextSelectionStart= start;
2787 _TextSelectionEnd= end;
2790 // ***************************************************************************
2791 void CViewText::disableStringSelection()
2793 _TextSelection= false;
2794 _TextSelectionStart= 0;
2795 _TextSelectionEnd= std::numeric_limits<uint>::max();
2798 // ***************************************************************************
2799 void CViewText::setStringSelectionSkipingSpace(uint stringId, const std::string &text, sint charStart, sint charEnd)
2801 sint quadStart= charStart;
2802 sint quadSize= charEnd-charStart;
2803 sint j;
2804 CUtfStringView sv(text);
2805 CUtfStringView::iterator it(sv.begin()), end(sv.end());
2806 for (j = 0; it != end && j < charStart; ++j, ++it)
2808 if(*it == (u32char)' ')
2809 quadStart--;
2811 for (j = charStart; it != end && j < charEnd; ++j, ++it)
2813 if(*it == (u32char)' ')
2814 quadSize--;
2816 // select what quad to skip
2817 CViewRenderer::getTextContext(_FontName)->setStringSelection(stringId, quadStart, quadSize); // FIXME: This assumes spaces are the only empty glyphs!
2820 // ***************************************************************************
2821 void CViewText::clearLines()
2823 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
2824 for(uint k = 0; k < _Lines.size(); ++k)
2826 _Lines[k]->clear(*TextContext);
2828 _Lines.clear();
2831 // ***************************************************************************
2832 uint CViewText::getNumLine() const
2834 if (_MultiLine)
2836 return (uint)_Lines.size();
2838 else
2840 return _Text.empty() ? 0 : 1;
2844 // ***************************************************************************
2845 uint CViewText::getFirstLineX() const
2847 return _FirstLineX / _Scale;
2850 // ***************************************************************************
2851 uint CViewText::getLastLineW () const
2853 if (!_Lines.empty())
2854 return (uint)_Lines.back()->getWidth() / _Scale;
2855 return 0;
2858 // ***************************************************************************
2859 void CViewText::setFirstLineX(sint firstLineX)
2861 _FirstLineX = firstLineX * _Scale;
2864 /////////////////////////////////////
2865 // CViewText::CLine implementation //
2866 /////////////////////////////////////
2868 // ***************************************************************************
2869 CViewText::CLine::CLine() : _NumChars(0),
2870 _NumSpaces(0),
2871 _SpaceWidth(0.f),
2872 _StringLine(0),
2873 _WidthWithoutSpaces(0.f),
2874 _EndSpaces(0),
2875 _HasLF(false)
2879 // ***************************************************************************
2880 void CViewText::CLine::addWord(const std::string &text, uint numSpaces, const CFormatInfo &wordFormat, float fontWidth, float tabWidth, NL3D::UTextContext &textContext)
2882 CWord word;
2883 word.build(text, textContext, numSpaces);
2884 word.Format= wordFormat;
2885 addWord(word, fontWidth, tabWidth);
2888 // ***************************************************************************
2889 void CViewText::CLine::addWord(const CWord &word, float fontWidth, float tabWidth)
2891 _Words.push_back(word);
2892 _NumChars += word.NumSpaces + uint(word.Info.StringLength);
2893 _NumSpaces += word.NumSpaces;
2894 if (fabsf(word.Info.StringLine) > fabsf(_StringLine))
2896 _StringLine = word.Info.StringLine;
2898 // the width of the line must reach at least the Tab
2899 _WidthWithoutSpaces= max(_WidthWithoutSpaces, word.Format.TabX * tabWidth);
2900 // append the text space
2901 _WidthWithoutSpaces += word.Info.StringWidth;
2904 // ***************************************************************************
2905 void CViewText::CLine::clear(NL3D::UTextContext &textContext)
2907 for(uint k = 0; k < _Words.size(); ++k)
2909 if (_Words[k].Index != 0xffffffff)
2910 textContext.erase(_Words[k].Index);
2912 _Words.clear();
2913 _NumChars = 0;
2914 _SpaceWidth = 0.f;
2917 // ***************************************************************************
2918 void CViewText::CLine::resetTextIndex()
2920 for(uint k = 0; k < _Words.size(); ++k)
2922 _Words[k].Index = 0xffffffff;
2926 // ***************************************************************************
2927 void CViewText::CWord::build(const std::string &text, NL3D::UTextContext &textContext, uint numSpaces)
2929 Text = text;
2930 NumSpaces = numSpaces;
2931 Index = textContext.textPush(text);
2932 Info = textContext.getStringInfo(Index);
2933 nlassert(Info.StringLength == CUtfStringView(text).count());
2936 // ***************************************************************************
2937 void CViewText::removeEndSpaces()
2939 sint i = (sint)_Text.size()-1;
2940 while ((i>=0) && ((_Text[i] < 0x20) || (_Text[i] == ' ')))
2942 i--;
2944 _Text.resize (i+1);
2947 // ***************************************************************************
2948 sint32 CViewText::getMaxUsedW() const
2950 static const char *spaceStr(" \t");
2951 static const char *lineFeedStr("\n");
2952 float maxWidth = 0;
2954 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
2955 TextContext->setHotSpot (UTextContext::BottomLeft);
2956 TextContext->setShaded (_Shadow);
2957 TextContext->setShadeOutline (_ShadowOutline);
2958 TextContext->setFontSize (_FontSize*_Scale);
2959 TextContext->setEmbolden (_Embolden);
2960 TextContext->setOblique (_Oblique);
2962 TCharPos linePos = 0;
2963 while (linePos < _Text.length())
2965 // Get the end of the line
2966 float lineWidth = 0;
2967 TCharPos lineEnd;
2968 lineEnd = _Text.find_first_of("\n", linePos);
2969 if (lineEnd == std::string::npos)
2971 lineEnd = _Text.length();
2974 std::string lineValue;
2975 lineValue = _Text.substr(linePos, lineEnd - linePos);
2977 TCharPos currPos = 0;
2978 while (currPos != lineValue.length())
2980 TCharPos spaceEnd;
2981 TCharPos wordEnd;
2982 uint numSpaces;
2984 // Skip spaces and count them
2985 spaceEnd = lineValue.find_first_not_of(spaceStr, currPos);
2986 if (spaceEnd == std::string::npos)
2988 spaceEnd = lineValue.length();
2990 numSpaces = (uint) (spaceEnd - currPos);
2992 // Get word until a space or a \n is encountered
2993 wordEnd = lineValue.find_first_of(spaceStr, spaceEnd);
2994 if (wordEnd == std::string::npos)
2996 wordEnd = lineValue.length();
2999 std::string wordValue;
3000 wordValue = lineValue.substr(spaceEnd, wordEnd - spaceEnd);
3002 // compute width of word
3003 UTextContext::CStringInfo si;
3004 si = TextContext->getStringInfo(wordValue);
3006 // compute size of spaces + word
3007 lineWidth += numSpaces * _SpaceWidth + si.StringWidth;
3009 currPos = wordEnd;
3012 // Update line width
3013 if (lineWidth > maxWidth)
3014 maxWidth = lineWidth;
3016 linePos = lineEnd+1;
3019 return (sint32)ceilf(maxWidth / _Scale);
3022 // ***************************************************************************
3023 sint32 CViewText::getMinUsedW() const
3025 static const char *spaceOrLineFeedStr(" \n\t");
3026 float maxWidth = 0.0f;
3028 // Not multi line ? Same size than min
3029 if (!_MultiLine)
3030 return getMaxUsedW();
3032 // If we can clip word, size of the largest word
3033 if (_TextMode == ClipWord)
3035 // No largest font parameter, return the font height
3036 return (sint32)ceilf(_FontHeight / _Scale);
3038 // If we can't clip the words, return the size of the largest word
3039 else if ((_TextMode == DontClipWord) || (_TextMode == Justified) || (_TextMode == Centered))
3041 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
3042 TextContext->setHotSpot (UTextContext::BottomLeft);
3043 TextContext->setShaded (_Shadow);
3044 TextContext->setShadeOutline (_ShadowOutline);
3045 TextContext->setFontSize (_FontSize*_Scale);
3046 TextContext->setEmbolden (_Embolden);
3047 TextContext->setOblique (_Oblique);
3049 // Current position in text
3050 TCharPos currPos = 0;
3051 while (currPos < _Text.length())
3053 // Current word
3054 std::string wordValue;
3055 UTextContext::CStringInfo si;
3056 TCharPos wordEnd;
3058 // Get word until a space or a \n is encountered
3059 currPos = _Text.find_first_not_of(spaceOrLineFeedStr, currPos);
3060 if (currPos == std::string::npos)
3061 break;
3062 wordEnd = _Text.find_first_of(spaceOrLineFeedStr, currPos);
3063 if (wordEnd == std::string::npos)
3064 wordEnd = _Text.length();
3066 // Get the word
3067 wordValue = _Text.substr(currPos, wordEnd - currPos);
3069 // Compute width of word
3070 si = TextContext->getStringInfo(wordValue);
3072 // Larger ?
3073 if (maxWidth < si.StringWidth)
3074 maxWidth = si.StringWidth;
3076 // Next word
3077 currPos = wordEnd;
3081 return ceilf(maxWidth / _Scale);
3084 // ***************************************************************************
3085 void CViewText::onInvalidateContent()
3087 _InvalidTextContext= true;
3089 if (_ParentElm)
3090 _ParentElm->invalidateCoords();
3092 invalidateCoords();
3095 // ***************************************************************************
3096 void CViewText::onInterfaceScaleChanged()
3098 _Scale = CWidgetManager::getInstance()->getInterfaceScale();
3100 computeFontSize ();
3101 invalidateContent();
3103 CViewBase::onInterfaceScaleChanged();
3106 // ***************************************************************************
3107 void CViewText::computeFontSize ()
3109 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
3110 TextContext->setHotSpot (UTextContext::BottomLeft);
3111 TextContext->setShaded (_Shadow);
3112 TextContext->setShadeOutline (_ShadowOutline);
3113 TextContext->setFontSize (_FontSize * _Scale);
3114 TextContext->setEmbolden (_Embolden);
3115 TextContext->setOblique (_Oblique);
3117 UTextContext::CStringInfo si = TextContext->getStringInfo("XO");
3118 float xoHeight = si.StringHeight;
3120 si = TextContext->getStringInfo("XO\xC3\x81\xC3\x83");
3121 float upHeight = si.StringHeight;
3123 si = TextContext->getStringInfo("XOgq");
3124 float downHeight = si.StringHeight;
3125 float legHeight = si.StringLine;
3127 nlassert(upHeight >= xoHeight);
3128 nlassert(downHeight >= xoHeight);
3129 float diff;
3130 if (downHeight > upHeight)
3132 diff = downHeight - xoHeight;
3134 else
3136 diff = upHeight - xoHeight;
3137 legHeight += upHeight - downHeight;
3140 _FontHeight = xoHeight + diff + diff;
3141 _FontLegHeight = legHeight;
3143 // Space width
3144 si = TextContext->getStringInfo(" ");
3145 _SpaceWidth = si.StringWidth;
3147 // Font Width
3148 si = TextContext->getStringInfo("O");
3149 _FontWidth = si.StringWidth;
3151 // Tab Width (used for {Txx})
3152 // if not set to "_", breaks item help window
3153 si = TextContext->getStringInfo("_");
3154 _TabWidth = si.StringWidth;
3158 // ***************************************************************************
3159 static inline bool isColorTag(const std::string &s, uint index, uint textSize)
3161 // Format is @{RGBA}
3162 if(s[index]=='@')
3164 if( textSize>index+1 && s[index+1]=='{')
3166 // verify 1st letter is a xdigit
3167 if( textSize>index+2 && isxdigit(s[index+2]))
3169 // We have good chance its a color tag. Do last verification
3170 if(textSize>index+6 && s[index+6]=='}')
3172 return true;
3178 return false;
3181 // ***************************************************************************
3182 // isColorTag must be ok.
3183 static inline CRGBA getColorTag(const std::string &s, uint &index)
3185 // extract the color string: "FABC"
3186 char tmpCol[5];
3187 for(uint i=0;i<4;i++)
3188 tmpCol[i]= (char)s[index+2+i];
3189 tmpCol[4]= 0;
3191 // Convert to color
3192 CRGBA color;
3193 uint pCol;
3194 sscanf(tmpCol, "%x", &pCol);
3195 // Transform 4 bits to 8 bit.
3196 color.R= (pCol>>12)&0xF; color.R+= color.R<<4;
3197 color.G= (pCol>>8)&0xF; color.G+= color.G<<4;
3198 color.B= (pCol>>4)&0xF; color.B+= color.B<<4;
3199 color.A= (pCol)&0xF; color.A+= color.A<<4;
3201 // skip tag
3202 index+= 7;
3204 return color;
3208 // ***************************************************************************
3209 const uint MaxTabDigit= 3;
3210 static inline bool isTabTag(const std::string &s, uint index, uint textSize)
3212 // Format is @{Tvalue}, where value ,1,2,3 digit.
3213 if(s[index]=='@')
3215 if( textSize>index+1 && s[index+1]=='{')
3217 if( textSize>index+2 && s[index+2]=='T')
3219 // We have good chance its a Tab tag. Do last verification
3220 for(uint i=4;i<4+MaxTabDigit;i++)
3222 if(textSize>index+i && s[index+i]=='}')
3224 return true;
3231 return false;
3234 // ***************************************************************************
3235 // isTabTag must be ok.
3236 static inline sint getTabTag(const std::string &s, uint &index)
3238 // extract the tab min X value
3239 char tmpTab[MaxTabDigit+1];
3240 uint i;
3241 for(i=0;i<MaxTabDigit;i++)
3243 if(s[index+3+i]=='}')
3244 break;
3245 tmpTab[i]= (char)s[index+3+i];
3247 tmpTab[i]= 0;
3249 // skip tag
3250 index+= 3+i+1;
3252 sint ret;
3253 fromString(tmpTab, ret);
3254 return ret;
3258 // ***************************************************************************
3259 static inline bool isTooltipTag(const std::string &s, uint index, uint textSize)
3261 // Format is @{Huitt*}
3262 if(s[index]=='@')
3264 if( textSize>index+1 && s[index+1]=='{')
3266 if( textSize>index+2 && s[index+2]=='H')
3268 uint i = 3;
3269 while (textSize>index+i && s[index+i]!='}')
3270 i++;
3272 if (textSize>index+i && s[index+i]=='}')
3273 return true;
3278 return false;
3281 // ***************************************************************************
3282 // isTooltipTag must be ok.
3283 static inline std::string getTooltipTag(const std::string &s, uint &index)
3285 std::string result;
3286 uint i = 3;
3287 while (s[index+i] != '}')
3289 result += s[index+i];
3290 i++;
3293 // skip tag
3294 index += i+1;
3296 return result;
3300 // ***************************************************************************
3301 void CViewText::buildFormatTagText(const std::string &text, std::string &textBuild, std::vector<CViewText::CFormatTag> &formatTags, std::vector<std::string> &tooltips)
3303 formatTags.clear();
3304 tooltips.clear();
3306 // Build the text without the formatTags, and get the color tags separately
3307 textBuild.reserve(text.size());
3308 uint textSize= (uint)text.size();
3309 // Must herit all the props from old tags.
3310 CViewText::CFormatTag precTag; // set default.
3311 precTag.Index = 0;
3312 for (uint i = 0; i < textSize;)
3314 if(isColorTag(text, i, textSize))
3316 // get old tag.
3317 CViewText::CFormatTag ct= precTag;
3318 // get new color and skip tag.
3319 ct.Color= getColorTag(text, i);
3320 ct.Index= (uint)textBuild.size();
3321 formatTags.push_back(ct);
3323 else if(isTabTag(text, i, textSize))
3325 // get old tag.
3326 CViewText::CFormatTag ct= precTag;
3327 // get new Tab and skip tag.
3328 ct.TabX= getTabTag(text, i);
3329 ct.Index= (uint)textBuild.size();
3330 formatTags.push_back(ct);
3332 else if(isTooltipTag(text, i, textSize))
3334 // get old tag.
3335 CViewText::CFormatTag ct= precTag;
3336 // get new Tab and skip tag.
3337 string uitt = getTooltipTag(text, i);
3338 if (uitt.empty())
3340 ct.IndexTt= -1;
3342 else
3344 ct.IndexTt= (uint)tooltips.size();
3345 tooltips.push_back(uitt);
3348 ct.Index= (uint)textBuild.size();
3349 formatTags.push_back(ct);
3351 else
3353 bool lineFeed= text[i]=='\n';
3355 // append to textBuild
3356 textBuild+= text[i];
3357 ++i;
3359 // if \n, reset tabulations
3360 if(lineFeed)
3362 CViewText::CFormatTag ct= precTag;
3363 ct.TabX= 0;
3364 ct.Index= (uint)textBuild.size();
3365 formatTags.push_back(ct);
3368 // bkup
3369 if(!formatTags.empty())
3370 precTag= formatTags.back();
3374 #ifdef RYZOM_LUA_UCSTRING
3375 // ***************************************************************************
3376 void CViewText::setTextFormatTagedAsUtf16(const ucstring &text)
3378 setTextFormatTaged(text.toUtf8());
3380 #endif
3382 // ***************************************************************************
3383 void CViewText::setTextFormatTaged(const std::string &text)
3385 if( text.empty() )
3386 return;
3388 // to allow cache (avoid infinite recurse in updateCoords() in some case), compute in temp
3389 std::string tempText;
3390 // static to avoid reallocation
3391 static std::vector<CFormatTag> tempFormatTags;
3392 static std::vector<std::string> tempTooltips;
3393 buildFormatTagText(text, tempText, tempFormatTags, tempTooltips);
3394 setCase (tempText, _CaseMode);
3396 // compare Tag arrays
3397 bool sameTagArray= false;
3398 if(_FormatTags.size()==tempFormatTags.size())
3400 sameTagArray= true;
3401 for(uint i=0;i<_FormatTags.size();i++)
3403 if(!_FormatTags[i].sameTag(tempFormatTags[i]))
3405 sameTagArray= false;
3406 break;
3411 // test transformed text with current one
3412 if(tempText!=_Text || !sameTagArray )
3414 // copy tags
3415 _FormatTags= tempFormatTags;
3416 // Copy to Text (preserve Memory)
3417 contReset(_Text);
3418 _Text = tempText;
3419 _HardText.clear();
3420 _TextLength = 0;
3422 CInterfaceGroup *parent = getParent();
3424 // Delete old dynamic tooltips
3425 for (uint i=0 ; i<_Tooltips.size() ; ++i)
3427 if (parent)
3428 parent->delCtrl(_Tooltips[i]);
3429 else
3430 delete _Tooltips[i];
3432 _Tooltips.clear();
3434 // Add new dynamic tooltips
3435 for (uint i=0 ; i<tempTooltips.size() ; ++i)
3437 CCtrlToolTip *pTooltip = new CCtrlToolTip(CCtrlToolTip::TCtorParam());
3438 pTooltip->setId(_Id+"_tt"+toString(i));
3439 pTooltip->setAvoidResizeParent(avoidResizeParent());
3440 pTooltip->setRenderLayer(getRenderLayer());
3441 std::string tempTooltipStr = tempTooltips[i];
3442 bool isI18N = NLMISC::startsWith(tempTooltipStr, "ui");
3443 pTooltip->setDefaultContextHelp(isI18N ? CI18N::get(tempTooltipStr) : tempTooltipStr);
3444 pTooltip->setParentPos(this);
3445 pTooltip->setParentPosRef(Hotspot_BR);
3446 pTooltip->setPosRef(Hotspot_BR);
3447 pTooltip->setToolTipParent(CCtrlBase::TTWindow);
3448 pTooltip->setToolTipParentPosRef(Hotspot_TTAuto);
3449 pTooltip->setToolTipPosRef(Hotspot_TTAuto);
3450 pTooltip->setActive(true);
3452 _Tooltips.push_back(pTooltip);
3454 if (parent)
3456 pTooltip->setParent(parent);
3457 parent->addCtrl(_Tooltips.back());
3461 if (parent)
3462 _Setuped = true;
3463 else
3464 _Setuped = false;
3466 invalidateContent ();
3469 // color format is available only if multilined
3470 if (!_MultiLine)
3471 nlwarning("ViewText isn't multilined : uc_hardtext_format will not act as wanted !\n%s", text.c_str());
3474 #ifdef RYZOM_LUA_UCSTRING
3475 void CViewText::setSingleLineTextFormatTagedAsUtf16(const ucstring &text)
3477 setSingleLineTextFormatTaged(text.toUtf8());
3479 #endif
3481 void CViewText::setSingleLineTextFormatTaged(const std::string &text)
3483 if( text.empty() )
3484 return;
3486 // to allow cache (avoid infinite recurse in updateCoords() in some case), compute in temp
3487 std::string tempText;
3488 static std::vector<CFormatTag> tempLetterColors;
3489 static std::vector<std::string> tempTooltips;
3491 // parse text
3492 buildFormatTagText(text, tempText, tempLetterColors, tempTooltips);
3493 setCase (tempText, _CaseMode);
3495 // decal for spaces (not inserted in VertexBuffer)
3496 uint textIndex = 0;
3497 uint spacesNb = 0;
3498 for(uint i=0; i<tempLetterColors.size(); i++)
3500 CFormatTag & formatTag = tempLetterColors[i];
3502 while(textIndex<formatTag.Index)
3504 if(tempText[textIndex] == ' ')
3505 spacesNb++;
3507 textIndex++;
3510 formatTag.Index -= spacesNb;
3513 // convert in ULetterColors
3514 NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName);
3515 ULetterColors * letterColors = TextContext->createLetterColors();
3516 for(uint i=0; i<tempLetterColors.size(); i++)
3518 const CFormatTag & formatTag = tempLetterColors[i];
3519 letterColors->pushLetterColor(formatTag.Index, formatTag.Color);
3522 // test transformed text with current one
3523 if(tempText!=_Text || !_LetterColors || !_LetterColors->isSameLetterColors(letterColors))
3525 _LetterColors = letterColors;
3527 TextContext->setLetterColors(letterColors, _Index);
3529 // Copy to Text (preserve Memory)
3530 contReset(_Text);
3531 _Text = tempText;
3532 _HardText.clear();
3533 _TextLength = 0;
3534 invalidateContent ();
3537 // this color format is available only if not multilined
3538 if (_MultiLine)
3539 nlwarning("ViewText is multilined : uc_hardtext_single_line_format will not act as wanted !\n%s", text.c_str());
3543 // ***************************************************************************
3544 bool CViewText::isFormatTagChange(uint textIndex, uint ctIndex) const
3546 if(ctIndex>=_FormatTags.size())
3547 return false;
3548 // return true if the textIndex is > (eg if some skip with spaces) or = (common case)
3549 return _FormatTags[ctIndex].Index <= textIndex;
3552 // ***************************************************************************
3553 void CViewText::getFormatTagChange(uint textIndex, uint &ctIndex, CFormatInfo &wordFormat) const
3555 // support the possible case with multiple color tags with same textIndex.
3556 while(ctIndex<_FormatTags.size() && _FormatTags[ctIndex].Index<=textIndex)
3558 // Take the last tag.
3559 wordFormat.Color= _FormatTags[ctIndex].Color;
3560 wordFormat.TabX= _FormatTags[ctIndex].TabX;
3561 wordFormat.IndexTt= _FormatTags[ctIndex].IndexTt;
3562 // skip it.
3563 ctIndex++;
3568 // ***************************************************************************
3570 void CViewText::setCaseMode (TCaseMode caseMode)
3572 _CaseMode = caseMode;
3573 setCase (_Text, _CaseMode);
3574 _TextLength = 0;
3577 // ***************************************************************************
3579 TCaseMode CViewText::getCaseMode () const
3581 return _CaseMode;
3584 // ***************************************************************************
3586 void CViewText::resetTextIndex()
3588 _Index = 0xffffffff;
3589 for(uint k = 0; k < _Lines.size(); ++k)
3590 _Lines[k]->resetTextIndex();
3593 // ***************************************************************************
3594 void CViewText::setup()
3596 _Setuped= true;
3598 // Add dynamic tooltips
3599 for (uint i=0 ; i<_Tooltips.size() ; ++i)
3601 CInterfaceGroup *parent = getParent();
3602 if (parent)
3604 _Tooltips[i]->setParent(parent);
3605 parent->addCtrl(_Tooltips.back());
3610 // ***************************************************************************
3611 void CViewText::serial(NLMISC::IStream &f)
3613 #define SERIAL_UINT(val) { uint32 tmp = (uint32) val; f.serial(tmp); val = (uint) tmp; }
3614 #define SERIAL_SINT(val) { sint32 tmp = (sint32) val; f.serial(tmp); val = (sint) tmp; }
3615 CViewBase::serial(f);
3617 int version = f.serialVersion(101);
3618 nlassert(version >= 100);
3620 f.serial(_Localized);
3621 SERIAL_SINT(_FontSize);
3622 SERIAL_UINT(_FontWidth);
3623 SERIAL_UINT(_TabWidth);
3624 SERIAL_UINT(_FontHeight);
3625 SERIAL_UINT(_FontLegHeight);
3626 f.serial(_SpaceWidth);
3627 f.serial(_Color);
3628 f.serial(_Shadow);
3629 f.serial(_ShadowOutline);
3630 f.serialEnum(_CaseMode);
3631 f.serial(_ShadowColor);
3632 f.serial(_LineMaxW);
3633 f.serial(_SingleLineTextClamped);
3634 f.serial(_MultiLine);
3635 f.serial(_MultiLineMaxWOnly);
3636 f.serial(_MultiLineClipEndSpace);
3637 f.serial(_AutoClampOffset);
3638 f.serialEnum(_TextMode);
3639 SERIAL_SINT(_MultiLineSpace);
3640 SERIAL_SINT(_LastMultiLineMaxW);
3641 f.serial(_MultiMaxLine);
3643 bool hasTag = !_FormatTags.empty();
3644 f.serial(hasTag);
3645 if (f.isReading())
3647 ucstring text;
3648 f.serial(text);
3649 if (hasTag)
3651 if (_MultiLine)
3653 setTextFormatTaged(text.toUtf8());
3655 else
3657 setSingleLineTextFormatTaged(text.toUtf8());
3660 else
3662 setText(text.toUtf8());
3665 else
3667 ucstring text = CUtfStringView(_Text).toUtf16();
3668 f.serial(text);
3671 #undef SERIAL_UINT
3672 #undef SERIAL_SINT
3676 // ***************************************************************************