2019-07-26 11:57:28 -07:00
/* Copyright (C) 2019 Wildfire Games.
2009-04-18 10:00:33 -07:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
2004-06-03 11:38:14 -07:00
# include "precompiled.h"
Move CGUI::GenerateText to CGUIText constructor, CGUI::DrawText to CGUIText::Draw, SGenerateTextImage from CGUI to CGUIText.
Makes GUI text construction 30x faster for empty strings, otherwise less
than 1% faster.
Split the constructor into smaller helper functions to reduce nesting
and improve readability.
Change m_GeneratedTexts from pointer to reference, so that one doesn't
have to keep track to delete it correctly in several places, without
having to resort to copy or move assignments but constructing in place.
Mark CGUIText as NONCOPYABLE and MOVABLE which is already implicitly the
case due to the CGUISpriteInstance members, refs 0a7d0ecdde/D2133,
D2163/3028551b91.
Differential Revision: https://code.wildfiregames.com/D2168
Prepared by the GUIText.h file split in 838889ab12 / D2167.
Comments By: Vladislav
Tested on: gcc 9, clang 8, VS2015, Jenkins
Inlining tested using: clang -Rpass=inline and gcc -Winline
This was SVN commit r22679.
2019-08-16 18:32:11 -07:00
# include "CGUIString.h"
2014-11-15 18:10:28 -08:00
Move CGUI::GenerateText to CGUIText constructor, CGUI::DrawText to CGUIText::Draw, SGenerateTextImage from CGUI to CGUIText.
Makes GUI text construction 30x faster for empty strings, otherwise less
than 1% faster.
Split the constructor into smaller helper functions to reduce nesting
and improve readability.
Change m_GeneratedTexts from pointer to reference, so that one doesn't
have to keep track to delete it correctly in several places, without
having to resort to copy or move assignments but constructing in place.
Mark CGUIText as NONCOPYABLE and MOVABLE which is already implicitly the
case due to the CGUISpriteInstance members, refs 0a7d0ecdde/D2133,
D2163/3028551b91.
Differential Revision: https://code.wildfiregames.com/D2168
Prepared by the GUIText.h file split in 838889ab12 / D2167.
Comments By: Vladislav
Tested on: gcc 9, clang 8, VS2015, Jenkins
Inlining tested using: clang -Rpass=inline and gcc -Winline
This was SVN commit r22679.
2019-08-16 18:32:11 -07:00
# include "graphics/FontMetrics.h"
2019-09-18 13:51:45 -07:00
# include "gui/CGUI.h"
2014-11-15 18:10:28 -08:00
# include "lib/utf8.h"
2006-06-01 19:10:27 -07:00
# include "ps/CLogger.h"
2004-05-28 21:06:50 -07:00
Move CGUI::GenerateText to CGUIText constructor, CGUI::DrawText to CGUIText::Draw, SGenerateTextImage from CGUI to CGUIText.
Makes GUI text construction 30x faster for empty strings, otherwise less
than 1% faster.
Split the constructor into smaller helper functions to reduce nesting
and improve readability.
Change m_GeneratedTexts from pointer to reference, so that one doesn't
have to keep track to delete it correctly in several places, without
having to resort to copy or move assignments but constructing in place.
Mark CGUIText as NONCOPYABLE and MOVABLE which is already implicitly the
case due to the CGUISpriteInstance members, refs 0a7d0ecdde/D2133,
D2163/3028551b91.
Differential Revision: https://code.wildfiregames.com/D2168
Prepared by the GUIText.h file split in 838889ab12 / D2167.
Comments By: Vladislav
Tested on: gcc 9, clang 8, VS2015, Jenkins
Inlining tested using: clang -Rpass=inline and gcc -Winline
This was SVN commit r22679.
2019-08-16 18:32:11 -07:00
# include <algorithm>
# include <array>
2004-06-10 19:20:59 -07:00
2016-06-07 05:02:33 -07:00
// List of word delimiter bounds
// The list contains ranges of word delimiters. The odd indexed chars are the start
2014-03-17 03:13:49 -07:00
// of a range, the even are the end of a range. The list must be sorted in INCREASING ORDER
2016-06-07 05:02:33 -07:00
static const int NUM_WORD_DELIMITERS = 4 * 2 ;
static const u16 WordDelimiters [ NUM_WORD_DELIMITERS ] = {
2014-03-17 03:13:49 -07:00
' ' , ' ' , // spaces
' - ' , ' - ' , // hyphens
2014-05-09 05:13:42 -07:00
0x3000 , 0x31FF , // ideographic symbols
0x3400 , 0x9FFF
2014-03-17 03:13:49 -07:00
// TODO add unicode blocks of other languages that don't use spaces
} ;
2004-05-28 21:06:50 -07:00
void CGUIString : : SFeedback : : Reset ( )
{
m_Images [ Left ] . clear ( ) ;
m_Images [ Right ] . clear ( ) ;
m_TextCalls . clear ( ) ;
m_SpriteCalls . clear ( ) ;
m_Size = CSize ( ) ;
2015-08-21 10:08:41 -07:00
m_NewLine = false ;
2004-05-28 21:06:50 -07:00
}
2019-08-21 03:12:33 -07:00
void CGUIString : : GenerateTextCall ( const CGUI & pGUI , SFeedback & Feedback , CStrIntern DefaultFont , const int & from , const int & to , const bool FirstLine , const IGUIObject * pObject ) const
2004-05-28 21:06:50 -07:00
{
// Reset width and height, because they will be determined with incrementation
// or comparisons.
Feedback . Reset ( ) ;
// Check out which text chunk this is within.
2015-07-12 01:09:55 -07:00
for ( const TextChunk & textChunk : m_TextChunks )
2004-05-28 21:06:50 -07:00
{
// Get the area that is overlapped by both the TextChunk and
// by the from/to inputted.
2015-07-12 01:09:55 -07:00
int _from = std : : max ( from , textChunk . m_From ) ;
int _to = std : : min ( to , textChunk . m_To ) ;
2013-09-13 16:49:46 -07:00
2015-07-12 01:09:55 -07:00
// If from is larger than to, then they are not overlapping
if ( _to = = _from & & textChunk . m_From = = textChunk . m_To )
2004-05-28 21:06:50 -07:00
{
// These should never be able to have more than one tag.
2015-07-12 01:09:55 -07:00
ENSURE ( textChunk . m_Tags . size ( ) = = 1 ) ;
2004-05-28 21:06:50 -07:00
2015-07-12 01:09:55 -07:00
// Icons and images are placed on exactly one position
// in the words-list, and they can be counted twice if placed
// on an edge. But there is always only one logical preference
// that we want. This check filters the unwanted.
2013-09-13 16:49:46 -07:00
// it's in the end of one word, and the icon
2004-08-30 19:09:58 -07:00
// should really belong to the beginning of the next one
2015-07-12 09:49:26 -07:00
if ( _to = = to & & to > = 1 & & to < ( int ) m_RawString . length ( ) )
2004-08-30 19:09:58 -07:00
{
2014-11-15 18:10:28 -08:00
if ( m_RawString [ to - 1 ] = = ' ' | |
m_RawString [ to - 1 ] = = ' - ' | |
m_RawString [ to - 1 ] = = ' \n ' )
2004-08-30 19:09:58 -07:00
continue ;
}
2008-07-13 14:22:03 -07:00
// This std::string is just a break
2004-08-30 19:09:58 -07:00
if ( _from = = from & & from > = 1 )
{
2014-11-15 18:10:28 -08:00
if ( m_RawString [ from ] = = ' \n ' & &
m_RawString [ from - 1 ] ! = ' \n ' & &
m_RawString [ from - 1 ] ! = ' ' & &
m_RawString [ from - 1 ] ! = ' - ' )
2004-08-30 19:09:58 -07:00
continue ;
}
2015-07-12 01:09:55 -07:00
const TextChunk : : Tag & tag = textChunk . m_Tags [ 0 ] ;
2016-06-07 05:02:33 -07:00
ENSURE ( tag . m_TagType = = TextChunk : : Tag : : TAG_IMGLEFT | |
tag . m_TagType = = TextChunk : : Tag : : TAG_IMGRIGHT | |
tag . m_TagType = = TextChunk : : Tag : : TAG_ICON ) ;
2014-11-15 18:10:28 -08:00
const std : : string & path = utf8_from_wstring ( tag . m_TagValue ) ;
2019-08-21 03:12:33 -07:00
if ( ! pGUI . HasIcon ( path ) )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
if ( pObject )
2015-01-22 12:36:24 -08:00
LOGERROR ( " Trying to use an icon, imgleft or imgright-tag with an undefined icon ( \" %s \" ). " , path . c_str ( ) ) ;
2014-11-15 18:10:28 -08:00
continue ;
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
switch ( tag . m_TagType )
2004-05-28 21:06:50 -07:00
{
2015-07-12 01:09:55 -07:00
case TextChunk : : Tag : : TAG_IMGLEFT :
2014-11-15 18:10:28 -08:00
Feedback . m_Images [ SFeedback : : Left ] . push_back ( path ) ;
break ;
2015-07-12 01:09:55 -07:00
case TextChunk : : Tag : : TAG_IMGRIGHT :
2014-11-15 18:10:28 -08:00
Feedback . m_Images [ SFeedback : : Right ] . push_back ( path ) ;
break ;
2015-07-12 01:09:55 -07:00
case TextChunk : : Tag : : TAG_ICON :
{
// We'll need to setup a text-call that will point
// to the icon, this is to be able to iterate
// through the text-calls without having to
// complex the structure virtually for nothing more.
Move CGUI::GenerateText to CGUIText constructor, CGUI::DrawText to CGUIText::Draw, SGenerateTextImage from CGUI to CGUIText.
Makes GUI text construction 30x faster for empty strings, otherwise less
than 1% faster.
Split the constructor into smaller helper functions to reduce nesting
and improve readability.
Change m_GeneratedTexts from pointer to reference, so that one doesn't
have to keep track to delete it correctly in several places, without
having to resort to copy or move assignments but constructing in place.
Mark CGUIText as NONCOPYABLE and MOVABLE which is already implicitly the
case due to the CGUISpriteInstance members, refs 0a7d0ecdde/D2133,
D2163/3028551b91.
Differential Revision: https://code.wildfiregames.com/D2168
Prepared by the GUIText.h file split in 838889ab12 / D2167.
Comments By: Vladislav
Tested on: gcc 9, clang 8, VS2015, Jenkins
Inlining tested using: clang -Rpass=inline and gcc -Winline
This was SVN commit r22679.
2019-08-16 18:32:11 -07:00
CGUIText : : STextCall TextCall ;
2004-08-30 19:09:58 -07:00
2015-07-12 01:09:55 -07:00
// Also add it to the sprites being rendered.
Move CGUI::GenerateText to CGUIText constructor, CGUI::DrawText to CGUIText::Draw, SGenerateTextImage from CGUI to CGUIText.
Makes GUI text construction 30x faster for empty strings, otherwise less
than 1% faster.
Split the constructor into smaller helper functions to reduce nesting
and improve readability.
Change m_GeneratedTexts from pointer to reference, so that one doesn't
have to keep track to delete it correctly in several places, without
having to resort to copy or move assignments but constructing in place.
Mark CGUIText as NONCOPYABLE and MOVABLE which is already implicitly the
case due to the CGUISpriteInstance members, refs 0a7d0ecdde/D2133,
D2163/3028551b91.
Differential Revision: https://code.wildfiregames.com/D2168
Prepared by the GUIText.h file split in 838889ab12 / D2167.
Comments By: Vladislav
Tested on: gcc 9, clang 8, VS2015, Jenkins
Inlining tested using: clang -Rpass=inline and gcc -Winline
This was SVN commit r22679.
2019-08-16 18:32:11 -07:00
CGUIText : : SSpriteCall SpriteCall ;
2004-05-28 21:06:50 -07:00
2015-07-12 01:09:55 -07:00
// Get Icon from icon database in pGUI
2019-08-21 03:12:33 -07:00
const SGUIIcon & icon = pGUI . GetIcon ( path ) ;
2004-05-28 21:06:50 -07:00
MOVABLE idiom, const CGUI struct maps, in place move construction instead of copying temporaries during CGUI XML loading and GenerateText.
Introduce MOVABLE idiom indicating that a class can use move semantics.
Make values of CGUI struct maps holding XML data const to ensure at the
root that the data is not modified.
Use NONCOPYABLE and MOVABLE for SGUIIcon and SGUIStyle to enforce the
non-copy policy on the compiler level (until someone changes the GUI
design to make modifications needed).
As indicated by that:
Replace copy operations by in place move operations for these CGUI
struct maps in the CGUI Xeromyces XML loading functions.
Replace copy operations by const references for CSize and SGUIIcon in
CGUIString::GenerateTextCall and CGUI::GenerateText.
This avoids copying of non primitive members, such as the strings and
containers of strings.
Further related cleanup to be kept separated for auditability.
Differential Revision: https://code.wildfiregames.com/D2163
Few comments on IRC by: wraitii, Itms
Tested on: gcc 9, Jenkins, partially VS2015
Refs #1984,
NONCOPYABLE CGUISpriteInstances: 0a7d0ecdde, 8f4f8e240f, c19f3608a5
NONCOPYABLE Image, Sprite: fb8032043b
NONCOPYABLE GUI page: 94c57085e9
NONCOPYABLE GUIManager: 7c2e9027c2
NONCOPYABLE macro: 16ccae10cd
This was SVN commit r22637.
2019-08-09 10:25:55 -07:00
const CSize & size = icon . m_Size ;
2004-05-28 21:06:50 -07:00
2015-07-12 01:09:55 -07:00
// append width, and make maximum height the height.
Feedback . m_Size . cx + = size . cx ;
Feedback . m_Size . cy = std : : max ( Feedback . m_Size . cy , size . cy ) ;
2010-10-29 21:02:42 -07:00
2015-07-12 01:09:55 -07:00
// These are also needed later
TextCall . m_Size = size ;
SpriteCall . m_Area = size ;
2010-10-29 21:02:42 -07:00
2015-07-12 01:09:55 -07:00
// Handle additional attributes
for ( const TextChunk : : Tag : : TagAttribute & tagAttrib : tag . m_TagAttributes )
{
if ( tagAttrib . attrib = = L " displace " & & ! tagAttrib . value . empty ( ) )
2014-11-15 18:10:28 -08:00
{
2015-07-12 01:09:55 -07:00
// Displace the sprite
CSize displacement ;
// Parse the value
2019-08-29 02:07:29 -07:00
if ( ! CGUI : : ParseString < CSize > ( & pGUI , tagAttrib . value , displacement ) )
2015-07-12 01:09:55 -07:00
LOGERROR ( " Error parsing 'displace' value for tag [ICON] " ) ;
else
SpriteCall . m_Area + = displacement ;
2004-10-13 19:32:26 -07:00
}
2015-07-12 01:09:55 -07:00
else if ( tagAttrib . attrib = = L " tooltip " )
SpriteCall . m_Tooltip = tagAttrib . value ;
else if ( tagAttrib . attrib = = L " tooltip_style " )
SpriteCall . m_TooltipStyle = tagAttrib . value ;
}
2004-10-13 19:32:26 -07:00
2015-07-12 01:09:55 -07:00
SpriteCall . m_Sprite = icon . m_SpriteName ;
SpriteCall . m_CellID = icon . m_CellID ;
2004-05-28 21:06:50 -07:00
2015-07-12 01:09:55 -07:00
// Add sprite call
2019-07-28 15:40:58 -07:00
Feedback . m_SpriteCalls . push_back ( std : : move ( SpriteCall ) ) ;
2004-05-28 21:06:50 -07:00
2015-07-12 01:09:55 -07:00
// Finalize text call
TextCall . m_pSpriteCall = & Feedback . m_SpriteCalls . back ( ) ;
// Add text call
Use NONCOPYABLE for most GUI classes and structs to have the compiler indicate unintended copies, refs 3028551b91 / D2163.
That is CChartData, CGUIList, CGUISeries, COListColumn, GUITooltip,
SGUIMessage, SSpriteCall, STextCall, SFeedback, IGUISetting,
CGUISetting, GUI, IGUIObject, IGUIScrollBar.
Drop copying GetSetting and SetSetting template functions for CGUIList,
CGUISeries, CClientArea, CGUIString.
Stop copying COListColumn.
Drop copying GUI<CClientArea>::GetSetting call in
IGUIObject::UpdateCachedSize() and four copying
GUI<CGUIString>::GetSetting calls in SetupText() functions.
Delete unused GUIRenderer IGLState class from 849f50a500 obsolete since
1f5b8f1c9a.
Differential Revision: https://code.wildfiregames.com/D2164
This was SVN commit r22638.
2019-08-09 17:04:17 -07:00
Feedback . m_TextCalls . emplace_back ( std : : move ( TextCall ) ) ;
2004-05-28 21:06:50 -07:00
2015-01-11 13:37:53 -08:00
break ;
2015-07-12 01:09:55 -07:00
}
2015-04-20 07:51:06 -07:00
NODEFAULT ;
2004-05-28 21:06:50 -07:00
}
}
2014-11-15 18:10:28 -08:00
else if ( _to > _from & & ! Feedback . m_NewLine )
2004-05-28 21:06:50 -07:00
{
Move CGUI::GenerateText to CGUIText constructor, CGUI::DrawText to CGUIText::Draw, SGenerateTextImage from CGUI to CGUIText.
Makes GUI text construction 30x faster for empty strings, otherwise less
than 1% faster.
Split the constructor into smaller helper functions to reduce nesting
and improve readability.
Change m_GeneratedTexts from pointer to reference, so that one doesn't
have to keep track to delete it correctly in several places, without
having to resort to copy or move assignments but constructing in place.
Mark CGUIText as NONCOPYABLE and MOVABLE which is already implicitly the
case due to the CGUISpriteInstance members, refs 0a7d0ecdde/D2133,
D2163/3028551b91.
Differential Revision: https://code.wildfiregames.com/D2168
Prepared by the GUIText.h file split in 838889ab12 / D2167.
Comments By: Vladislav
Tested on: gcc 9, clang 8, VS2015, Jenkins
Inlining tested using: clang -Rpass=inline and gcc -Winline
This was SVN commit r22679.
2019-08-16 18:32:11 -07:00
CGUIText : : STextCall TextCall ;
2004-05-28 21:06:50 -07:00
// Set defaults
TextCall . m_Font = DefaultFont ;
TextCall . m_UseCustomColor = false ;
2014-11-15 18:10:28 -08:00
TextCall . m_String = m_RawString . substr ( _from , _to - _from ) ;
2013-09-13 16:49:46 -07:00
2004-05-28 21:06:50 -07:00
// Go through tags and apply changes.
2015-07-12 01:09:55 -07:00
for ( const TextChunk : : Tag & tag : textChunk . m_Tags )
2004-05-28 21:06:50 -07:00
{
2015-07-12 01:09:55 -07:00
switch ( tag . m_TagType )
2004-05-28 21:06:50 -07:00
{
2015-07-12 01:09:55 -07:00
case TextChunk : : Tag : : TAG_COLOR :
2004-05-28 21:06:50 -07:00
TextCall . m_UseCustomColor = true ;
2013-09-13 16:49:46 -07:00
2019-08-29 02:07:29 -07:00
if ( ! CGUI : : ParseString < CGUIColor > ( & pGUI , tag . m_TagValue , TextCall . m_Color ) & & pObject )
2015-01-22 12:36:24 -08:00
LOGERROR ( " Error parsing the value of a [color]-tag in GUI text when reading object \" %s \" . " , pObject - > GetPresentableName ( ) . c_str ( ) ) ;
2014-11-15 18:10:28 -08:00
break ;
2015-07-12 01:09:55 -07:00
case TextChunk : : Tag : : TAG_FONT :
2004-08-30 19:09:58 -07:00
// TODO Gee: (2004-08-15) Check if Font exists?
2015-07-12 01:09:55 -07:00
TextCall . m_Font = CStrIntern ( utf8_from_wstring ( tag . m_TagValue ) ) ;
2014-11-15 18:10:28 -08:00
break ;
default :
2015-01-22 12:31:30 -08:00
LOGERROR ( " Encountered unexpected tag applied to text " ) ;
2014-11-15 18:10:28 -08:00
break ;
2004-05-28 21:06:50 -07:00
}
}
2004-08-27 15:08:30 -07:00
// Calculate the size of the font
2004-05-28 21:06:50 -07:00
CSize size ;
2004-09-02 22:48:47 -07:00
int cx , cy ;
2013-10-18 08:53:07 -07:00
CFontMetrics font ( TextCall . m_Font ) ;
2012-02-25 09:14:47 -08:00
font . CalculateStringSize ( TextCall . m_String . c_str ( ) , cx , cy ) ;
2004-09-04 13:35:12 -07:00
// For anything other than the first line, the line spacing
// needs to be considered rather than just the height of the text
2014-11-15 18:10:28 -08:00
if ( ! FirstLine )
2004-09-04 13:35:12 -07:00
cy = font . GetLineSpacing ( ) ;
2004-09-02 22:48:47 -07:00
size . cx = ( float ) cx ;
size . cy = ( float ) cy ;
2004-05-28 21:06:50 -07:00
2004-08-27 15:08:30 -07:00
// Append width, and make maximum height the height.
2004-05-28 21:06:50 -07:00
Feedback . m_Size . cx + = size . cx ;
2008-07-13 14:22:03 -07:00
Feedback . m_Size . cy = std : : max ( Feedback . m_Size . cy , size . cy ) ;
2004-05-28 21:06:50 -07:00
2004-09-05 19:21:21 -07:00
// These are also needed later
2004-05-28 21:06:50 -07:00
TextCall . m_Size = size ;
2014-11-15 18:10:28 -08:00
if ( ! TextCall . m_String . empty ( ) & & TextCall . m_String [ 0 ] = = ' \n ' )
Feedback . m_NewLine = true ;
2004-05-28 21:06:50 -07:00
// Add text-chunk
Use NONCOPYABLE for most GUI classes and structs to have the compiler indicate unintended copies, refs 3028551b91 / D2163.
That is CChartData, CGUIList, CGUISeries, COListColumn, GUITooltip,
SGUIMessage, SSpriteCall, STextCall, SFeedback, IGUISetting,
CGUISetting, GUI, IGUIObject, IGUIScrollBar.
Drop copying GetSetting and SetSetting template functions for CGUIList,
CGUISeries, CClientArea, CGUIString.
Stop copying COListColumn.
Drop copying GUI<CClientArea>::GetSetting call in
IGUIObject::UpdateCachedSize() and four copying
GUI<CGUIString>::GetSetting calls in SetupText() functions.
Delete unused GUIRenderer IGLState class from 849f50a500 obsolete since
1f5b8f1c9a.
Differential Revision: https://code.wildfiregames.com/D2164
This was SVN commit r22638.
2019-08-09 17:04:17 -07:00
Feedback . m_TextCalls . emplace_back ( std : : move ( TextCall ) ) ;
2004-05-28 21:06:50 -07:00
}
}
}
2014-11-15 18:10:28 -08:00
bool CGUIString : : TextChunk : : Tag : : SetTagType ( const CStrW & tagtype )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
TagType t = GetTagType ( tagtype ) ;
if ( t = = TAG_INVALID )
return false ;
2004-05-28 21:06:50 -07:00
2014-11-15 18:10:28 -08:00
m_TagType = t ;
return true ;
}
2004-05-28 21:06:50 -07:00
2015-08-21 10:08:41 -07:00
CGUIString : : TextChunk : : Tag : : TagType CGUIString : : TextChunk : : Tag : : GetTagType ( const CStrW & tagtype ) const
2014-11-15 18:10:28 -08:00
{
if ( tagtype = = L " color " )
return TAG_COLOR ;
if ( tagtype = = L " font " )
return TAG_FONT ;
if ( tagtype = = L " icon " )
return TAG_ICON ;
if ( tagtype = = L " imgleft " )
return TAG_IMGLEFT ;
if ( tagtype = = L " imgright " )
return TAG_IMGRIGHT ;
return TAG_INVALID ;
2004-05-28 21:06:50 -07:00
}
2004-09-05 19:21:21 -07:00
void CGUIString : : SetValue ( const CStrW & str )
2004-05-28 21:06:50 -07:00
{
2011-02-16 12:40:15 -08:00
m_OriginalString = str ;
2004-05-28 21:06:50 -07:00
m_TextChunks . clear ( ) ;
m_Words . clear ( ) ;
2014-11-15 18:10:28 -08:00
m_RawString . clear ( ) ;
2004-05-28 21:06:50 -07:00
// Current Text Chunk
CGUIString : : TextChunk CurrentTextChunk ;
2014-11-15 18:10:28 -08:00
CurrentTextChunk . m_From = 0 ;
int l = str . length ( ) ;
int rawpos = 0 ;
CStrW tag ;
std : : vector < CStrW > tags ;
bool closing = false ;
for ( int p = 0 ; p < l ; + + p )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
TextChunk : : Tag tag_ ;
switch ( str [ p ] )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
case L ' [ ' :
CurrentTextChunk . m_To = rawpos ;
// Add the current chunks if it is not empty
if ( CurrentTextChunk . m_From ! = rawpos )
2004-05-28 21:06:50 -07:00
m_TextChunks . push_back ( CurrentTextChunk ) ;
2014-11-15 18:10:28 -08:00
CurrentTextChunk . m_From = rawpos ;
2004-05-28 21:06:50 -07:00
2014-11-15 18:10:28 -08:00
closing = false ;
if ( + + p = = l )
2004-05-28 21:06:50 -07:00
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Partial tag at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
if ( str [ p ] = = L ' / ' )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
closing = true ;
if ( tags . empty ( ) )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Encountered closing tag without having any open tags. At %d in '%s' " , p , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
}
if ( + + p = = l )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Partial closing tag at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
}
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
tag . clear ( ) ;
// Parse tag
for ( ; p < l & & str [ p ] ! = L ' ] ' ; + + p )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
CStrW name , param ;
switch ( str [ p ] )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
case L ' ' :
if ( closing ) // We still parse them to make error handling cleaner
2015-01-22 12:37:38 -08:00
LOGERROR ( " Closing tags do not support parameters (at pos %d '%s') " , p , utf8_from_wstring ( str ) ) ;
2004-05-28 21:06:50 -07:00
2014-11-15 18:10:28 -08:00
// parse something="something else"
for ( + + p ; p < l & & str [ p ] ! = L ' = ' ; + + p )
name . push_back ( str [ p ] ) ;
2004-05-28 21:06:50 -07:00
2014-11-15 18:10:28 -08:00
if ( p = = l )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Parameter without value at pos %d '%s' " , p , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
}
2017-09-01 13:04:53 -07:00
FALLTHROUGH ;
2014-11-15 18:10:28 -08:00
case L ' = ' :
// parse a quoted parameter
if ( closing ) // We still parse them to make error handling cleaner
2015-01-22 12:37:38 -08:00
LOGERROR ( " Closing tags do not support parameters (at pos %d '%s') " , p , utf8_from_wstring ( str ) ) ;
2004-05-28 21:06:50 -07:00
2014-11-15 18:10:28 -08:00
if ( + + p = = l )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Expected parameter, got end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
if ( str [ p ] ! = L ' " ' )
2004-05-28 21:06:50 -07:00
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Unquoted parameters are not supported (at pos %d '%s') " , p , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
}
for ( + + p ; p < l & & str [ p ] ! = L ' " ' ; + + p )
{
switch ( str [ p ] )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
case L ' \\ ' :
if ( + + p = = l )
2004-05-28 21:06:50 -07:00
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Escape character at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
2004-05-28 21:06:50 -07:00
}
2014-12-12 17:08:29 -08:00
// NOTE: We do not support \n in tag parameters
2017-09-01 13:04:53 -07:00
FALLTHROUGH ;
2014-11-15 18:10:28 -08:00
default :
param . push_back ( str [ p ] ) ;
2004-05-28 21:06:50 -07:00
}
}
2014-11-15 18:10:28 -08:00
if ( ! name . empty ( ) )
{
TextChunk : : Tag : : TagAttribute a = { name , param } ;
tag_ . m_TagAttributes . push_back ( a ) ;
}
else
tag_ . m_TagValue = param ;
break ;
default :
tag . push_back ( str [ p ] ) ;
break ;
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
}
2004-05-28 21:06:50 -07:00
2014-11-15 18:10:28 -08:00
if ( ! tag_ . SetTagType ( tag ) )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Invalid tag '%s' at %d in '%s' " , utf8_from_wstring ( tag ) , p , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
}
if ( ! closing )
{
if ( tag_ . m_TagType = = TextChunk : : Tag : : TAG_IMGRIGHT
| | tag_ . m_TagType = = TextChunk : : Tag : : TAG_IMGLEFT
| | tag_ . m_TagType = = TextChunk : : Tag : : TAG_ICON )
2004-05-28 21:06:50 -07:00
{
2014-11-15 18:10:28 -08:00
TextChunk FreshTextChunk = { rawpos , rawpos } ;
FreshTextChunk . m_Tags . push_back ( tag_ ) ;
m_TextChunks . push_back ( FreshTextChunk ) ;
}
else
{
tags . push_back ( tag ) ;
CurrentTextChunk . m_Tags . push_back ( tag_ ) ;
}
}
else
{
if ( tag ! = tags . back ( ) )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Closing tag '%s' does not match last opened tag '%s' at %d in '%s' " , utf8_from_wstring ( tag ) , utf8_from_wstring ( tags . back ( ) ) , p , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
tags . pop_back ( ) ;
CurrentTextChunk . m_Tags . pop_back ( ) ;
2004-05-28 21:06:50 -07:00
}
2014-11-15 18:10:28 -08:00
break ;
case L ' \\ ' :
if ( + + p = = l )
{
2015-01-22 12:37:38 -08:00
LOGERROR ( " Escape character at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-15 18:10:28 -08:00
break ;
}
2014-12-12 17:08:29 -08:00
if ( str [ p ] = = L ' n ' )
{
+ + rawpos ;
m_RawString . push_back ( L ' \n ' ) ;
break ;
}
2017-09-01 13:04:53 -07:00
FALLTHROUGH ;
2014-11-15 18:10:28 -08:00
default :
+ + rawpos ;
m_RawString . push_back ( str [ p ] ) ;
break ;
2004-05-28 21:06:50 -07:00
}
}
2014-11-15 18:10:28 -08:00
// Add the chunk after the last tag
if ( CurrentTextChunk . m_From ! = rawpos )
{
CurrentTextChunk . m_To = rawpos ;
m_TextChunks . push_back ( CurrentTextChunk ) ;
}
2004-05-28 21:06:50 -07:00
// Add a delimiter at start and at end, it helps when
// processing later, because we don't have make exceptions for
// those cases.
m_Words . push_back ( 0 ) ;
2014-03-17 03:13:49 -07:00
// Add word boundaries in increasing order
for ( u32 i = 0 ; i < m_RawString . length ( ) ; + + i )
2004-05-28 21:06:50 -07:00
{
2014-03-17 03:13:49 -07:00
wchar_t c = m_RawString [ i ] ;
if ( c = = ' \n ' )
{
m_Words . push_back ( ( int ) i ) ;
m_Words . push_back ( ( int ) i + 1 ) ;
continue ;
}
2016-06-07 05:02:33 -07:00
for ( int n = 0 ; n < NUM_WORD_DELIMITERS ; n + = 2 )
2014-03-17 03:13:49 -07:00
{
2016-06-07 05:02:33 -07:00
if ( c < = WordDelimiters [ n + 1 ] )
2014-03-17 03:13:49 -07:00
{
2016-06-07 05:02:33 -07:00
if ( c > = WordDelimiters [ n ] )
2014-03-17 03:13:49 -07:00
m_Words . push_back ( ( int ) i + 1 ) ;
2016-06-07 05:02:33 -07:00
// assume the WordDelimiters list is stored in increasing order
2014-03-17 03:13:49 -07:00
break ;
}
}
2004-05-28 21:06:50 -07:00
}
2014-03-17 03:13:49 -07:00
m_Words . push_back ( ( int ) m_RawString . length ( ) ) ;
2004-05-28 21:06:50 -07:00
2004-08-30 19:09:58 -07:00
// Remove duplicates (only if larger than 2)
2014-11-15 18:10:28 -08:00
if ( m_Words . size ( ) < = 2 )
return ;
2015-08-21 10:08:41 -07:00
m_Words . erase ( std : : unique ( m_Words . begin ( ) , m_Words . end ( ) ) , m_Words . end ( ) ) ;
2004-11-07 13:30:47 -08:00
}