2017-09-01 22:04:53 +02:00
/* Copyright (C) 2017 Wildfire Games.
2009-04-18 19:00:33 +02: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 20:38:14 +02:00
# include "precompiled.h"
2014-11-16 03:10:28 +01:00
# include <algorithm>
2004-05-29 06:06:50 +02:00
# include "GUI.h"
2014-11-16 03:10:28 +01:00
# include "lib/utf8.h"
2013-10-18 17:53:07 +02:00
# include "graphics/FontMetrics.h"
2006-06-02 04:10:27 +02:00
# include "ps/CLogger.h"
2004-05-29 06:06:50 +02:00
2004-06-11 04:20:59 +02:00
2016-06-07 14:02:33 +02:00
// List of word delimiter bounds
// The list contains ranges of word delimiters. The odd indexed chars are the start
2014-03-17 11:13:49 +01:00
// of a range, the even are the end of a range. The list must be sorted in INCREASING ORDER
2016-06-07 14:02:33 +02:00
static const int NUM_WORD_DELIMITERS = 4 * 2 ;
static const u16 WordDelimiters [ NUM_WORD_DELIMITERS ] = {
2014-03-17 11:13:49 +01:00
' ' , ' ' , // spaces
' - ' , ' - ' , // hyphens
2014-05-09 14:13:42 +02:00
0x3000 , 0x31FF , // ideographic symbols
0x3400 , 0x9FFF
2014-03-17 11:13:49 +01:00
// TODO add unicode blocks of other languages that don't use spaces
} ;
2004-05-29 06:06:50 +02: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 19:08:41 +02:00
m_NewLine = false ;
2004-05-29 06:06:50 +02:00
}
2015-08-21 19:08:41 +02: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-29 06:06:50 +02: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 10:09:55 +02:00
for ( const TextChunk & textChunk : m_TextChunks )
2004-05-29 06:06:50 +02:00
{
// Get the area that is overlapped by both the TextChunk and
// by the from/to inputted.
2015-07-12 10:09:55 +02:00
int _from = std : : max ( from , textChunk . m_From ) ;
int _to = std : : min ( to , textChunk . m_To ) ;
2013-09-14 01:49:46 +02:00
2015-07-12 10:09:55 +02:00
// If from is larger than to, then they are not overlapping
if ( _to = = _from & & textChunk . m_From = = textChunk . m_To )
2004-05-29 06:06:50 +02:00
{
// These should never be able to have more than one tag.
2015-07-12 10:09:55 +02:00
ENSURE ( textChunk . m_Tags . size ( ) = = 1 ) ;
2004-05-29 06:06:50 +02:00
2015-07-12 10:09:55 +02: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-14 01:49:46 +02:00
// it's in the end of one word, and the icon
2004-08-31 04:09:58 +02:00
// should really belong to the beginning of the next one
2015-07-12 18:49:26 +02:00
if ( _to = = to & & to > = 1 & & to < ( int ) m_RawString . length ( ) )
2004-08-31 04:09:58 +02:00
{
2014-11-16 03:10:28 +01:00
if ( m_RawString [ to - 1 ] = = ' ' | |
m_RawString [ to - 1 ] = = ' - ' | |
m_RawString [ to - 1 ] = = ' \n ' )
2004-08-31 04:09:58 +02:00
continue ;
}
2008-07-13 23:22:03 +02:00
// This std::string is just a break
2004-08-31 04:09:58 +02:00
if ( _from = = from & & from > = 1 )
{
2014-11-16 03:10:28 +01:00
if ( m_RawString [ from ] = = ' \n ' & &
m_RawString [ from - 1 ] ! = ' \n ' & &
m_RawString [ from - 1 ] ! = ' ' & &
m_RawString [ from - 1 ] ! = ' - ' )
2004-08-31 04:09:58 +02:00
continue ;
}
2015-07-12 10:09:55 +02:00
const TextChunk : : Tag & tag = textChunk . m_Tags [ 0 ] ;
2016-06-07 14:02:33 +02: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-16 03:10:28 +01:00
const std : : string & path = utf8_from_wstring ( tag . m_TagValue ) ;
if ( ! pGUI - > IconExists ( path ) )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
if ( pObject )
2015-01-22 21:36:24 +01:00
LOGERROR ( " Trying to use an icon, imgleft or imgright-tag with an undefined icon ( \" %s \" ). " , path . c_str ( ) ) ;
2014-11-16 03:10:28 +01:00
continue ;
2004-05-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
switch ( tag . m_TagType )
2004-05-29 06:06:50 +02:00
{
2015-07-12 10:09:55 +02:00
case TextChunk : : Tag : : TAG_IMGLEFT :
2014-11-16 03:10:28 +01:00
Feedback . m_Images [ SFeedback : : Left ] . push_back ( path ) ;
break ;
2015-07-12 10:09:55 +02:00
case TextChunk : : Tag : : TAG_IMGRIGHT :
2014-11-16 03:10:28 +01:00
Feedback . m_Images [ SFeedback : : Right ] . push_back ( path ) ;
break ;
2015-07-12 10:09:55 +02: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.
SGUIText : : STextCall TextCall ;
2004-08-31 04:09:58 +02:00
2015-07-12 10:09:55 +02:00
// Also add it to the sprites being rendered.
SGUIText : : SSpriteCall SpriteCall ;
2004-05-29 06:06:50 +02:00
2015-07-12 10:09:55 +02:00
// Get Icon from icon database in pGUI
SGUIIcon icon = pGUI - > GetIcon ( path ) ;
2004-05-29 06:06:50 +02:00
2015-07-12 10:09:55 +02:00
CSize size = icon . m_Size ;
2004-05-29 06:06:50 +02:00
2015-07-12 10:09:55 +02: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-30 06:02:42 +02:00
2015-07-12 10:09:55 +02:00
// These are also needed later
TextCall . m_Size = size ;
SpriteCall . m_Area = size ;
2010-10-30 06:02:42 +02:00
2015-07-12 10:09:55 +02:00
// Handle additional attributes
for ( const TextChunk : : Tag : : TagAttribute & tagAttrib : tag . m_TagAttributes )
{
if ( tagAttrib . attrib = = L " displace " & & ! tagAttrib . value . empty ( ) )
2014-11-16 03:10:28 +01:00
{
2015-07-12 10:09:55 +02:00
// Displace the sprite
CSize displacement ;
// Parse the value
if ( ! GUI < CSize > : : ParseString ( tagAttrib . value , displacement ) )
LOGERROR ( " Error parsing 'displace' value for tag [ICON] " ) ;
else
SpriteCall . m_Area + = displacement ;
2004-10-14 04:32:26 +02:00
}
2015-07-12 10:09:55 +02: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-14 04:32:26 +02:00
2015-07-12 10:09:55 +02:00
SpriteCall . m_Sprite = icon . m_SpriteName ;
SpriteCall . m_CellID = icon . m_CellID ;
2004-05-29 06:06:50 +02:00
2015-07-12 10:09:55 +02:00
// Add sprite call
Feedback . m_SpriteCalls . push_back ( SpriteCall ) ;
2004-05-29 06:06:50 +02:00
2015-07-12 10:09:55 +02:00
// Finalize text call
TextCall . m_pSpriteCall = & Feedback . m_SpriteCalls . back ( ) ;
// Add text call
Feedback . m_TextCalls . push_back ( TextCall ) ;
2004-05-29 06:06:50 +02:00
2015-01-11 22:37:53 +01:00
break ;
2015-07-12 10:09:55 +02:00
}
2015-04-20 16:51:06 +02:00
NODEFAULT ;
2004-05-29 06:06:50 +02:00
}
}
2014-11-16 03:10:28 +01:00
else if ( _to > _from & & ! Feedback . m_NewLine )
2004-05-29 06:06:50 +02:00
{
SGUIText : : STextCall TextCall ;
// Set defaults
TextCall . m_Font = DefaultFont ;
TextCall . m_UseCustomColor = false ;
2014-11-16 03:10:28 +01:00
TextCall . m_String = m_RawString . substr ( _from , _to - _from ) ;
2013-09-14 01:49:46 +02:00
2004-05-29 06:06:50 +02:00
// Go through tags and apply changes.
2015-07-12 10:09:55 +02:00
for ( const TextChunk : : Tag & tag : textChunk . m_Tags )
2004-05-29 06:06:50 +02:00
{
2015-07-12 10:09:55 +02:00
switch ( tag . m_TagType )
2004-05-29 06:06:50 +02:00
{
2015-07-12 10:09:55 +02:00
case TextChunk : : Tag : : TAG_COLOR :
2004-05-29 06:06:50 +02:00
TextCall . m_UseCustomColor = true ;
2013-09-14 01:49:46 +02:00
2015-07-12 10:09:55 +02:00
if ( ! GUI < CColor > : : ParseString ( tag . m_TagValue , TextCall . m_Color ) & & pObject )
2015-01-22 21:36:24 +01:00
LOGERROR ( " Error parsing the value of a [color]-tag in GUI text when reading object \" %s \" . " , pObject - > GetPresentableName ( ) . c_str ( ) ) ;
2014-11-16 03:10:28 +01:00
break ;
2015-07-12 10:09:55 +02:00
case TextChunk : : Tag : : TAG_FONT :
2004-08-31 04:09:58 +02:00
// TODO Gee: (2004-08-15) Check if Font exists?
2015-07-12 10:09:55 +02:00
TextCall . m_Font = CStrIntern ( utf8_from_wstring ( tag . m_TagValue ) ) ;
2014-11-16 03:10:28 +01:00
break ;
default :
2015-01-22 21:31:30 +01:00
LOGERROR ( " Encountered unexpected tag applied to text " ) ;
2014-11-16 03:10:28 +01:00
break ;
2004-05-29 06:06:50 +02:00
}
}
2004-08-28 00:08:30 +02:00
// Calculate the size of the font
2004-05-29 06:06:50 +02:00
CSize size ;
2004-09-03 07:48:47 +02:00
int cx , cy ;
2013-10-18 17:53:07 +02:00
CFontMetrics font ( TextCall . m_Font ) ;
2012-02-25 18:14:47 +01:00
font . CalculateStringSize ( TextCall . m_String . c_str ( ) , cx , cy ) ;
2004-09-04 22:35:12 +02: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-16 03:10:28 +01:00
if ( ! FirstLine )
2004-09-04 22:35:12 +02:00
cy = font . GetLineSpacing ( ) ;
2004-09-03 07:48:47 +02:00
size . cx = ( float ) cx ;
size . cy = ( float ) cy ;
2004-05-29 06:06:50 +02:00
2004-08-28 00:08:30 +02:00
// Append width, and make maximum height the height.
2004-05-29 06:06:50 +02:00
Feedback . m_Size . cx + = size . cx ;
2008-07-13 23:22:03 +02:00
Feedback . m_Size . cy = std : : max ( Feedback . m_Size . cy , size . cy ) ;
2004-05-29 06:06:50 +02:00
2004-09-06 04:21:21 +02:00
// These are also needed later
2004-05-29 06:06:50 +02:00
TextCall . m_Size = size ;
2014-11-16 03:10:28 +01:00
if ( ! TextCall . m_String . empty ( ) & & TextCall . m_String [ 0 ] = = ' \n ' )
Feedback . m_NewLine = true ;
2004-05-29 06:06:50 +02:00
// Add text-chunk
Feedback . m_TextCalls . push_back ( TextCall ) ;
}
}
}
2014-11-16 03:10:28 +01:00
bool CGUIString : : TextChunk : : Tag : : SetTagType ( const CStrW & tagtype )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
TagType t = GetTagType ( tagtype ) ;
if ( t = = TAG_INVALID )
return false ;
2004-05-29 06:06:50 +02:00
2014-11-16 03:10:28 +01:00
m_TagType = t ;
return true ;
}
2004-05-29 06:06:50 +02:00
2015-08-21 19:08:41 +02:00
CGUIString : : TextChunk : : Tag : : TagType CGUIString : : TextChunk : : Tag : : GetTagType ( const CStrW & tagtype ) const
2014-11-16 03:10:28 +01: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-29 06:06:50 +02:00
}
2004-09-06 04:21:21 +02:00
void CGUIString : : SetValue ( const CStrW & str )
2004-05-29 06:06:50 +02:00
{
2011-02-16 21:40:15 +01:00
m_OriginalString = str ;
2004-05-29 06:06:50 +02:00
m_TextChunks . clear ( ) ;
m_Words . clear ( ) ;
2014-11-16 03:10:28 +01:00
m_RawString . clear ( ) ;
2004-05-29 06:06:50 +02:00
// Current Text Chunk
CGUIString : : TextChunk CurrentTextChunk ;
2014-11-16 03:10:28 +01: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-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
TextChunk : : Tag tag_ ;
switch ( str [ p ] )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
case L ' [ ' :
CurrentTextChunk . m_To = rawpos ;
// Add the current chunks if it is not empty
if ( CurrentTextChunk . m_From ! = rawpos )
2004-05-29 06:06:50 +02:00
m_TextChunks . push_back ( CurrentTextChunk ) ;
2014-11-16 03:10:28 +01:00
CurrentTextChunk . m_From = rawpos ;
2004-05-29 06:06:50 +02:00
2014-11-16 03:10:28 +01:00
closing = false ;
if ( + + p = = l )
2004-05-29 06:06:50 +02:00
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Partial tag at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
2004-05-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
if ( str [ p ] = = L ' / ' )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
closing = true ;
if ( tags . empty ( ) )
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Encountered closing tag without having any open tags. At %d in '%s' " , p , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
}
if ( + + p = = l )
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Partial closing tag at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
}
2004-05-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
tag . clear ( ) ;
// Parse tag
for ( ; p < l & & str [ p ] ! = L ' ] ' ; + + p )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
CStrW name , param ;
switch ( str [ p ] )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
case L ' ' :
if ( closing ) // We still parse them to make error handling cleaner
2015-01-22 21:37:38 +01:00
LOGERROR ( " Closing tags do not support parameters (at pos %d '%s') " , p , utf8_from_wstring ( str ) ) ;
2004-05-29 06:06:50 +02:00
2014-11-16 03:10:28 +01:00
// parse something="something else"
for ( + + p ; p < l & & str [ p ] ! = L ' = ' ; + + p )
name . push_back ( str [ p ] ) ;
2004-05-29 06:06:50 +02:00
2014-11-16 03:10:28 +01:00
if ( p = = l )
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Parameter without value at pos %d '%s' " , p , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
}
2017-09-01 22:04:53 +02:00
FALLTHROUGH ;
2014-11-16 03:10:28 +01:00
case L ' = ' :
// parse a quoted parameter
if ( closing ) // We still parse them to make error handling cleaner
2015-01-22 21:37:38 +01:00
LOGERROR ( " Closing tags do not support parameters (at pos %d '%s') " , p , utf8_from_wstring ( str ) ) ;
2004-05-29 06:06:50 +02:00
2014-11-16 03:10:28 +01:00
if ( + + p = = l )
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Expected parameter, got end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
2004-05-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
if ( str [ p ] ! = L ' " ' )
2004-05-29 06:06:50 +02:00
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Unquoted parameters are not supported (at pos %d '%s') " , p , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
}
for ( + + p ; p < l & & str [ p ] ! = L ' " ' ; + + p )
{
switch ( str [ p ] )
2004-05-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01:00
case L ' \\ ' :
if ( + + p = = l )
2004-05-29 06:06:50 +02:00
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Escape character at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
2004-05-29 06:06:50 +02:00
}
2014-12-13 02:08:29 +01:00
// NOTE: We do not support \n in tag parameters
2017-09-01 22:04:53 +02:00
FALLTHROUGH ;
2014-11-16 03:10:28 +01:00
default :
param . push_back ( str [ p ] ) ;
2004-05-29 06:06:50 +02:00
}
}
2014-11-16 03:10:28 +01: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-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
}
2004-05-29 06:06:50 +02:00
2014-11-16 03:10:28 +01:00
if ( ! tag_ . SetTagType ( tag ) )
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Invalid tag '%s' at %d in '%s' " , utf8_from_wstring ( tag ) , p , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01: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-29 06:06:50 +02:00
{
2014-11-16 03:10:28 +01: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 21:37:38 +01: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-16 03:10:28 +01:00
break ;
2004-05-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
tags . pop_back ( ) ;
CurrentTextChunk . m_Tags . pop_back ( ) ;
2004-05-29 06:06:50 +02:00
}
2014-11-16 03:10:28 +01:00
break ;
case L ' \\ ' :
if ( + + p = = l )
{
2015-01-22 21:37:38 +01:00
LOGERROR ( " Escape character at end of string '%s' " , utf8_from_wstring ( str ) ) ;
2014-11-16 03:10:28 +01:00
break ;
}
2014-12-13 02:08:29 +01:00
if ( str [ p ] = = L ' n ' )
{
+ + rawpos ;
m_RawString . push_back ( L ' \n ' ) ;
break ;
}
2017-09-01 22:04:53 +02:00
FALLTHROUGH ;
2014-11-16 03:10:28 +01:00
default :
+ + rawpos ;
m_RawString . push_back ( str [ p ] ) ;
break ;
2004-05-29 06:06:50 +02:00
}
}
2014-11-16 03:10:28 +01:00
// Add the chunk after the last tag
if ( CurrentTextChunk . m_From ! = rawpos )
{
CurrentTextChunk . m_To = rawpos ;
m_TextChunks . push_back ( CurrentTextChunk ) ;
}
2004-05-29 06:06:50 +02: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 11:13:49 +01:00
// Add word boundaries in increasing order
for ( u32 i = 0 ; i < m_RawString . length ( ) ; + + i )
2004-05-29 06:06:50 +02:00
{
2014-03-17 11:13:49 +01: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 14:02:33 +02:00
for ( int n = 0 ; n < NUM_WORD_DELIMITERS ; n + = 2 )
2014-03-17 11:13:49 +01:00
{
2016-06-07 14:02:33 +02:00
if ( c < = WordDelimiters [ n + 1 ] )
2014-03-17 11:13:49 +01:00
{
2016-06-07 14:02:33 +02:00
if ( c > = WordDelimiters [ n ] )
2014-03-17 11:13:49 +01:00
m_Words . push_back ( ( int ) i + 1 ) ;
2016-06-07 14:02:33 +02:00
// assume the WordDelimiters list is stored in increasing order
2014-03-17 11:13:49 +01:00
break ;
}
}
2004-05-29 06:06:50 +02:00
}
2014-03-17 11:13:49 +01:00
m_Words . push_back ( ( int ) m_RawString . length ( ) ) ;
2004-05-29 06:06:50 +02:00
2004-08-31 04:09:58 +02:00
// Remove duplicates (only if larger than 2)
2014-11-16 03:10:28 +01:00
if ( m_Words . size ( ) < = 2 )
return ;
2015-08-21 19:08:41 +02:00
m_Words . erase ( std : : unique ( m_Words . begin ( ) , m_Words . end ( ) ) , m_Words . end ( ) ) ;
2004-11-07 22:30:47 +01:00
}