// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#ifndef NL_VIEW_TEXT_H
#define NL_VIEW_TEXT_H
#include "view_base.h"
#include "../misc.h"
#include "nel/3d/u_text_context.h"
class CCtrlToolTip;
/**
* class implementing a text view
* \author Matthieu 'TrapII' Besson
* \author Nicolas Vizerie
* \author Nevrax France
* \date 2002
*/
class CViewText : public CViewBase
{
public:
enum TTextMode { ClipWord, DontClipWord, Justified };
public:
DECLARE_UI_CLASS(CViewText)
/// Constructor
CViewText (const TCtorParam ¶m);
/// Constructor
CViewText (const std::string& id, const std::string Text="", sint FontSize=12,
NLMISC::CRGBA Color=NLMISC::CRGBA(255,255,255), bool Shadow=false);
virtual ~CViewText();
CViewText &operator=(const CViewText &vt);
void parseTextOptions (xmlNodePtr cur);
bool parse (xmlNodePtr cur, CInterfaceGroup * parentGroup);
virtual uint32 getMemory() { return sizeof(*this)+_Id.size(); }
/// Updating
virtual void draw ();
void updateTextContext ();
virtual void checkCoords();
virtual void updateCoords();
virtual void onAddToGroup();
/// From CInterfaceElement
sint32 getMaxUsedW() const;
sint32 getMinUsedW() const;
/// Accessors
/// Set
void setText (const ucstring &text);
void setFontSize (sint nFontSize);
void setColor (const NLMISC::CRGBA &color);
void setShadow (bool bShadow);
void setShadowColor (const NLMISC::CRGBA &color);
void setLineMaxW (sint nMaxW, bool invalidate=true);
void setMultiLine (bool bMultiLine);
void setMultiLineSpace (sint nMultiLineSpace);
void setMultiLineMaxWOnly (bool state);
void setMultiLineClipEndSpace (bool state); // use it for multiline edit box for instance
void setFirstLineX (uint firstLineX);
void setMultiMaxLine(uint l) { _MultiMaxLine = l; }
// Force only a subset of letter to be displayed. Default is 0/0xFFFFFFFF
void enableStringSelection(uint start, uint end);
void disableStringSelection();
/// Get
ucstring getText() const { return _Text; }
sint getFontSize() const;
NLMISC::CRGBA getColor() { return _Color; }
bool getShadow() { return _Shadow; }
NLMISC::CRGBA getShadowColor() { return _ShadowColor; }
sint getLineMaxW() const { return _LineMaxW; }
bool getMultiLine() const { return _MultiLine; }
sint getMultiLineSpace() const { return _MultiLineSpace; }
bool getMultiLineMaxWOnly() const { return _MultiLineMaxWOnly; }
uint32 getMultiMaxLine() const { return _MultiMaxLine; }
// get current Hint font width, in pixels
uint getFontWidth() const;
// get current font height, in pixels
uint getFontHeight() const;
// get current font leg height, in pixels
uint getFontLegHeight() const;
// Set the display mode (supported with multiline only for now)
void setTextMode(TTextMode mode);
TTextMode getTextMode() const { return _TextMode; }
uint getNumLine() const;
uint getFirstLineX() const;
uint getLastLineW () const;
void setUnderlined (bool underlined) { _Underlined = underlined; }
bool getUnderlined () const { return _Underlined; }
// true if the viewText is a single line clamped.
bool isSingleLineTextClamped() const {return _SingleLineTextClamped;}
// Character positions
/** Get position of the ith character, position are relative to the BR corner of the text.
* \param lineEnd. When set to true, return the coordinate of the previous line if the index is at the start of a line.
* When looking at standard edit box, we see that if a line is split accross to line with no
* This also returns the height of the line
*/
void getCharacterPositionFromIndex(sint index, bool lineEnd, sint &x, sint &y, sint &height) const;
/** From a coordinate relative to the BR BR corner of the text, return the index of a character.
* If no character is found at the given position, the closest character is returned (first or last character, for the line or the whole text)
*/
void getCharacterIndexFromPosition(sint x, sint y, uint &index, bool &lineEnd) const;
/** From a character index, get the index of the line it belongs to, or -1 if the index is invalid
* \param cursorDisplayedAtEndOfPreviousLine true if the cursor is displayed at the end of the previous line that match its index
*/
sint getLineFromIndex(uint index, bool cursorDisplayedAtEndOfPreviousLine = true) const;
/// From a line number, get the character at which it starts, or -1 if invalid
sint getLineStartIndex(uint line) const;
/// From a line number, get the character at which it ends (not including any '\n' ), or -1 if invalid
void getLineEndIndex(uint line, sint &index, bool &endOfPreviousLine) const;
std::string getHardText() const { std::string result; _Text.toString (result); return result; }
void setHardText (const std::string &ht);
std::string getColorAsString() const;
void setColorAsString(const std::string &ht);
NLMISC::CRGBA getColorRGBA() const;
void setColorRGBA(NLMISC::CRGBA col);
virtual sint32 getAlpha() const { return _Color.A; }
virtual void setAlpha (sint32 a) { _ShadowColor.A = _Color.A = (uint8)a; }
/** Setup a Text with Format Tags. Text is store without color/format tags, and special array is allocated for Format association
*/
void setTextFormatTaged(const ucstring &text);
void setSingleLineTextFormatTaged(const ucstring &text);
// Remove end space
void removeEndSpaces();
// Reset the text index because the text context has changed
void resetTextIndex();
// Case mode
void setCaseMode (TCaseMode caseMode);
TCaseMode getCaseMode () const;
// OverExtendViewText
void setOverExtendViewText(bool state) {_OverExtendViewText= state;}
bool getOverExtendViewText() const {return _OverExtendViewText;}
// OverExtendViewTextUseParentRect
void setOverExtendViewTextUseParentRect(bool state) {_OverExtendViewTextUseParentRect= state;}
bool getOverExtendViewTextUseParentRect() const {return _OverExtendViewTextUseParentRect;}
// see if text ellipsis if done at right side of the text
bool isClampRight() const { return _ClampRight; }
int luaSetLineMaxW(CLuaState &ls);
REFLECT_EXPORT_START(CViewText, CViewBase)
REFLECT_STRING("hardtext", getHardText, setHardText);
REFLECT_UCSTRING("uc_hardtext", getText, setText);
REFLECT_UCSTRING("uc_hardtext_format", getText, setTextFormatTaged);
REFLECT_UCSTRING("uc_hardtext_single_line_format", getText, setSingleLineTextFormatTaged);
REFLECT_STRING ("color", getColorAsString, setColorAsString);
REFLECT_RGBA ("color_rgba", getColorRGBA, setColorRGBA);
REFLECT_SINT32 ("alpha", getAlpha, setAlpha);
REFLECT_BOOL ("overExtendViewText", getOverExtendViewText, setOverExtendViewText);
REFLECT_BOOL ("overExtendViewTextUseParentRect", getOverExtendViewTextUseParentRect, setOverExtendViewTextUseParentRect);
REFLECT_LUA_METHOD("setLineMaxW", luaSetLineMaxW);
REFLECT_EXPORT_END
virtual void serial(NLMISC::IStream &f);
protected:
/// Text to display.
ucstring _Text;
/// index of the computed String associated to this text control
uint _Index;
/// info on the computed String associated to this text control
NL3D::UTextContext::CStringInfo _Info;
/// the font size
sint _FontSize;
// width of the font in pixel. Just a Hint for tabing format (computed with '_')
uint _FontWidth;
// height of the font in pixel.
// use getFontHeight
uint _FontHeight;
uint _FontLegHeight;
float _SpaceWidth;
/// the text color
NLMISC::CRGBA _Color;
/// the shadow mode
bool _Shadow;
/// the case mode
TCaseMode _CaseMode;
/// the text shadow color
NLMISC::CRGBA _ShadowColor;
/// Is the line (under p loop) should be considered at bottom (if false bottom is under p leg)
/// maxw for the line/multiline
sint32 _LineMaxW;
/// For single line, true if the text is clamped (ie displayed with "...")
bool _SingleLineTextClamped;
/// Multiple lines handling
bool _MultiLine;
bool _MultiLineMaxWOnly;
bool _MultiLineClipEndSpace;
uint8 _AutoClampOffset;
TTextMode _TextMode;
sint _MultiLineSpace;
sint _LastMultiLineMaxW;
uint32 _MultiMaxLine;
/// FormatTag handling
struct CFormatInfo
{
// The color to change
NLMISC::CRGBA Color;
// The Tabulation to apply, in number of "_" characters.
uint TabX;
// Index in vector
sint IndexTt;
CFormatInfo()
{
Color= NLMISC::CRGBA::White;
TabX= 0;
IndexTt = -1;
}
bool operator==(const CFormatInfo &o) const {return Color==o.Color && TabX==o.TabX && IndexTt==o.IndexTt;}
bool operator!=(const CFormatInfo &o) const {return !operator==(o);}
};
struct CFormatTag : public CFormatInfo
{
uint Index;
// compare 2 tags, not a tag and a CFormatInfo
bool sameTag(const CFormatTag &o) const
{
return CFormatInfo::operator==(o) && Index==o.Index;
}
};
std::vector _FormatTags;
/// Get the current maxW for multiline, accordgin to parent and _MultiLineOptionMaxW
sint getCurrentMultiLineMaxW() const;
NL3D::ULetterColors * _LetterColors;
private:
// A word in a line
class CWord
{
public:
// default ctor
CWord(uint numSpaces = 0) : Index(0), NumSpaces(numSpaces) {}
ucstring Text;
uint Index; // index of the info for this word
NL3D::UTextContext::CStringInfo Info;
uint NumSpaces; // number of spaces before this word
// The specialized color/format of this word. White if none
CFormatInfo Format;
public:
// build from a string, using the current text context
void build(const ucstring &text, uint numSpaces= 0);
};
typedef std::vector TWordVect;
// A line of text (which is made of one word with space, or of several words with no spaces in them)
class CLine : public NLMISC::CRefCount
{
public:
// ctor
CLine();
// Clear the line & remove text contexts
void clear();
// Add a new word (and its context) in the line + a number of spaces to append at the end of the line
void addWord(const ucstring &word, uint numSpaces, const CFormatInfo &wordFormat, uint fontWidth);
void addWord(const CWord &word, uint fontWidth);
uint getNumWords() const { return _Words.size(); }
CWord &getWord(uint index) { return _Words[index]; }
float getSpaceWidth() const { return _SpaceWidth; }
void setSpaceWidth(float width) { _SpaceWidth = width; }
// Get the number of chars in the line, not counting the end spaces, but couting the spaces in words
uint getNumChars() const { return _NumChars; }
// Get the total number of spaces between words (not including those in words, but there should not be if text is justified)
uint getNumSpaces() const { return _NumSpaces; }
float getStringLine() const { return _StringLine; }
float getWidthWithoutSpaces() const { return _WidthWithoutSpaces; }
// get total width including spaces, but not including end spaces
float getWidth() const { return _WidthWithoutSpaces + _SpaceWidth * _NumSpaces; }
// Get the number of spaces at the end of the line
void setEndSpaces(uint numSpaces) { _EndSpaces = numSpaces; }
// Set the number of spaces at the end of the line
uint getEndSpaces() const { return _EndSpaces; }
// Test if there's a line feed at the end of the line
bool getLF() const { return _HasLF; }
void setLF(bool lf) { _HasLF = lf; }
void resetTextIndex();
private:
TWordVect _Words;
uint _NumChars;
uint _NumSpaces;
float _SpaceWidth; // width of a space, in pixels (used with multispace alignment)
float _StringLine;
float _WidthWithoutSpaces; // width without space (see the Field NumSpaces in the CWord class).
// NB : space inserted inside a word are counted, however!
uint _EndSpaces; // spaces at the end of the line
bool _HasLF; // a linefeed is at end of line (no breaking due to line full)
};
/// NB : we keep pointers on lines (each line contains a vector, that we don't want to be copied, and this occurs as the vector of lines grows..)
typedef NLMISC::CSmartPtr TLineSPtr;
typedef std::vector TLinePtVect;
private:
/** Data of the updated text for multiline. It is built from the _Text field in the updateTextContext member function,
* and is used to perform the draw
*/
TLinePtVect _Lines;
/// if true, and if the view text is isSingleLineTextClamped(), then an over will be drawn, with the text
bool _OverExtendViewText : 1;
/// if true and _OverExtendViewText true too, use the parent rectangle to know if must display the over or not
bool _OverExtendViewTextUseParentRect : 1;
/// Letter selection handling
bool _AutoClamp : 1;
bool _ClampRight : 1;
bool _TextSelection : 1;
bool _InvalidTextContext : 1;
bool _Underlined : 1;
bool _ContinuousUpdate : 1;
bool _Setuped : 1;
uint _TextSelectionStart;
uint _TextSelectionEnd;
// First line X coordinate
uint _FirstLineX;
/// Dynamic tooltips
std::vector _Tooltips;
private:
void setup ();
void setupDefault ();
void setStringSelectionSkipingSpace(uint stringId, const ucstring &text, sint charStart, sint charEnd);
// void pushString(const ucstring &str, bool deleteSpaceAtStart = false);
/// \from CInterfaceElement
void onInvalidateContent();
// may append a new line, and append a word to the last line (no spaces)
void flushWordInLine(ucstring &ucCurrentWord, bool &linePushed, const CFormatInfo &wordFormat);
// Clear all the lines and free their datas
void clearLines();
// Update in the case of a multiline text
void updateTextContextMultiLine(uint nMaxWidth);
// Update in the case of a multiline text with justification
void updateTextContextMultiLineJustified(uint nMaxWidth, bool expandSpaces);
// Recompute font size info
void computeFontSize ();
// used for "donctClipWord" case in updateTextContextMultiLineJustified(). currLine is reseted
void addDontClipWordLine(std::vector &currLine);
// FormatTag build.
static void buildFormatTagText(const ucstring &text, ucstring &textBuild, std::vector &formatTags, std::vector &tooltips);
// FormatTag parsing.
bool isFormatTagChange(uint textIndex, uint ctIndex) const;
void getFormatTagChange(uint textIndex, uint &ctIndex, CFormatInfo &wordFormat) const;
};
// change case of a string
void setCase (ucstring &str, TCaseMode mode);
#endif // NL_VIEW_TEXT_H
/* End of view_text.h */