phoenix-firestorm/indra/llui/lltextbox.cpp

439 lines
9.9 KiB
C++

/**
* @file lltextbox.cpp
* @brief A text display widget
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
* $License$
*/
#include "linden_common.h"
#include "lltextbox.h"
#include "llerror.h"
#include "llgl.h"
#include "llui.h"
#include "lluictrlfactory.h"
#include "llcontrol.h"
#include "llfocusmgr.h"
#include "llstl.h"
#include <boost/tokenizer.hpp>
LLTextBox::LLTextBox(const LLString& name, const LLRect& rect, const LLString& text,
const LLFontGL* font, BOOL mouse_opaque)
: LLUICtrl(name, rect, mouse_opaque, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP ),
mTextColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
mDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
mBackgroundColor( LLUI::sColorsGroup->getColor( "DefaultBackgroundColor" ) ),
mBorderColor( LLUI::sColorsGroup->getColor( "DefaultHighlightLight" ) ),
mBackgroundVisible( FALSE ),
mBorderVisible( FALSE ),
mDropshadowVisible( TRUE ),
mBorderDropShadowVisible( FALSE ),
mHPad(0),
mVPad(0),
mHAlign( LLFontGL::LEFT ),
mVAlign( LLFontGL::TOP ),
mClickedCallback(NULL),
mCallbackUserData(NULL)
{
// TomY TODO Nuke this eventually
setText( !text.empty() ? text : name );
mFontGL = font ? font : LLFontGL::sSansSerifSmall;
setTabStop(FALSE);
}
LLTextBox::LLTextBox(const LLString& name, const LLString& text, F32 max_width,
const LLFontGL* font, BOOL mouse_opaque) :
LLUICtrl(name, LLRect(0, 0, 1, 1), mouse_opaque, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP),
mFontGL(font ? font : LLFontGL::sSansSerifSmall),
mTextColor(LLUI::sColorsGroup->getColor("LabelTextColor")),
mDisabledColor(LLUI::sColorsGroup->getColor("LabelDisabledColor")),
mBackgroundColor(LLUI::sColorsGroup->getColor("DefaultBackgroundColor")),
mBorderColor(LLUI::sColorsGroup->getColor("DefaultHighlightLight")),
mBackgroundVisible(FALSE),
mBorderVisible(FALSE),
mDropshadowVisible(TRUE),
mBorderDropShadowVisible(FALSE),
mHPad(0),
mVPad(0),
mHAlign(LLFontGL::LEFT),
mVAlign( LLFontGL::TOP ),
mClickedCallback(NULL),
mCallbackUserData(NULL)
{
setWrappedText(!text.empty() ? text : name, max_width);
reshapeToFitText();
setTabStop(FALSE);
}
LLTextBox::~LLTextBox()
{
}
// virtual
EWidgetType LLTextBox::getWidgetType() const
{
return WIDGET_TYPE_TEXT_BOX;
}
// virtual
LLString LLTextBox::getWidgetTag() const
{
return LL_TEXT_BOX_TAG;
}
BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
// HACK: Only do this if there actually is a click callback, so that
// overly large text boxes in the older UI won't start eating clicks.
if (mClickedCallback)
{
handled = TRUE;
// Route future Mouse messages here preemptively. (Release on mouse up.)
gFocusMgr.setMouseCapture( this, NULL );
if (mSoundFlags & MOUSE_DOWN)
{
make_ui_sound("UISndClick");
}
}
return handled;
}
BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
// We only handle the click if the click both started and ended within us
// HACK: Only do this if there actually is a click callback, so that
// overly large text boxes in the older UI won't start eating clicks.
if (mClickedCallback
&& this == gFocusMgr.getMouseCapture())
{
handled = TRUE;
// Release the mouse
gFocusMgr.setMouseCapture( NULL, NULL );
if (mSoundFlags & MOUSE_UP)
{
make_ui_sound("UISndClickRelease");
}
// DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
// If mouseup in the widget, it's been clicked
if (mClickedCallback)
{
(*mClickedCallback)( mCallbackUserData );
}
}
return handled;
}
void LLTextBox::setText(const LLString& text)
{
mText.assign(text);
setLineLengths();
}
void LLTextBox::setLineLengths()
{
mLineLengthList.clear();
LLString::size_type cur = 0;
LLString::size_type len = mText.getWString().size();
while (cur < len)
{
LLString::size_type end = mText.getWString().find('\n', cur);
LLString::size_type runLen;
if (end == LLString::npos)
{
runLen = len - cur;
cur = len;
}
else
{
runLen = end - cur;
cur = end + 1; // skip the new line character
}
mLineLengthList.push_back( (S32)runLen );
}
}
void LLTextBox::setWrappedText(const LLString& in_text, F32 max_width)
{
if (max_width < 0.0)
{
max_width = (F32)getRect().getWidth();
}
LLWString wtext = utf8str_to_wstring(in_text);
LLWString final_wtext;
LLWString::size_type cur = 0;;
LLWString::size_type len = wtext.size();
while (cur < len)
{
LLWString::size_type end = wtext.find('\n', cur);
if (end == LLWString::npos)
{
end = len;
}
LLWString::size_type runLen = end - cur;
if (runLen > 0)
{
LLWString run(wtext, cur, runLen);
LLWString::size_type useLen =
mFontGL->maxDrawableChars(run.c_str(), max_width, runLen, TRUE);
final_wtext.append(wtext, cur, useLen);
cur += useLen;
}
if (cur < len)
{
if (wtext[cur] == '\n')
{
cur += 1;
}
final_wtext += '\n';
}
}
LLString final_text = wstring_to_utf8str(final_wtext);
setText(final_text);
}
S32 LLTextBox::getTextPixelWidth()
{
S32 max_line_width = 0;
if( mLineLengthList.size() > 0 )
{
S32 cur_pos = 0;
for (std::vector<S32>::iterator iter = mLineLengthList.begin();
iter != mLineLengthList.end(); ++iter)
{
S32 line_length = *iter;
S32 line_width = mFontGL->getWidth( mText.getWString().c_str(), cur_pos, line_length );
if( line_width > max_line_width )
{
max_line_width = line_width;
}
cur_pos += line_length+1;
}
}
else
{
max_line_width = mFontGL->getWidth(mText.getWString().c_str());
}
return max_line_width;
}
S32 LLTextBox::getTextPixelHeight()
{
S32 num_lines = mLineLengthList.size();
if( num_lines < 1 )
{
num_lines = 1;
}
return (S32)(num_lines * mFontGL->getLineHeight());
}
void LLTextBox::setValue(const LLSD& value )
{
setText(value.asString());
}
LLSD LLTextBox::getValue() const
{
return LLSD(getText());
}
BOOL LLTextBox::setTextArg( const LLString& key, const LLString& text )
{
mText.setArg(key, text);
setLineLengths();
return TRUE;
}
void LLTextBox::draw()
{
if( getVisible() )
{
if (mBorderVisible)
{
gl_rect_2d_offset_local(getLocalRect(), 2, FALSE);
}
if( mBorderDropShadowVisible )
{
static LLColor4 color_drop_shadow = LLUI::sColorsGroup->getColor("ColorDropShadow");
static S32 drop_shadow_tooltip = LLUI::sConfigGroup->getS32("DropShadowTooltip");
gl_drop_shadow(0, mRect.getHeight(), mRect.getWidth(), 0,
color_drop_shadow, drop_shadow_tooltip);
}
if (mBackgroundVisible)
{
LLRect r( 0, mRect.getHeight(), mRect.getWidth(), 0 );
gl_rect_2d( r, mBackgroundColor );
}
S32 text_x = 0;
switch( mHAlign )
{
case LLFontGL::LEFT:
text_x = mHPad;
break;
case LLFontGL::HCENTER:
text_x = mRect.getWidth() / 2;
break;
case LLFontGL::RIGHT:
text_x = mRect.getWidth() - mHPad;
break;
}
S32 text_y = mRect.getHeight() - mVPad;
if ( getEnabled() )
{
drawText( text_x, text_y, mTextColor );
}
else
{
drawText( text_x, text_y, mDisabledColor );
}
if (sDebugRects)
{
drawDebugRect();
}
}
}
void LLTextBox::reshape(S32 width, S32 height, BOOL called_from_parent)
{
// reparse line lengths
setText(mText);
LLView::reshape(width, height, called_from_parent);
}
void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color )
{
if( !mLineLengthList.empty() )
{
S32 cur_pos = 0;
for (std::vector<S32>::iterator iter = mLineLengthList.begin();
iter != mLineLengthList.end(); ++iter)
{
S32 line_length = *iter;
mFontGL->render(mText.getWString(), cur_pos, (F32)x, (F32)y, color,
mHAlign, mVAlign,
mDropshadowVisible ? LLFontGL::DROP_SHADOW : LLFontGL::NORMAL,
line_length, mRect.getWidth(), NULL, TRUE );
cur_pos += line_length + 1;
y -= llfloor(mFontGL->getLineHeight());
}
}
else
{
mFontGL->render(mText.getWString(), 0, (F32)x, (F32)y, color,
mHAlign, mVAlign,
mDropshadowVisible ? LLFontGL::DROP_SHADOW : LLFontGL::NORMAL,
S32_MAX, mRect.getWidth(), NULL, TRUE);
}
}
void LLTextBox::reshapeToFitText()
{
S32 width = getTextPixelWidth();
S32 height = getTextPixelHeight();
reshape( width + 2 * mHPad, height + 2 * mVPad );
}
// virtual
LLXMLNodePtr LLTextBox::getXML(bool save_children) const
{
LLXMLNodePtr node = LLUICtrl::getXML();
// Attributes
node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mFontGL));
node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign));
addColorXML(node, mTextColor, "text_color", "LabelTextColor");
addColorXML(node, mDisabledColor, "disabled_color", "LabelDisabledColor");
addColorXML(node, mBackgroundColor, "bg_color", "DefaultBackgroundColor");
addColorXML(node, mBorderColor, "border_color", "DefaultHighlightLight");
node->createChild("bg_visible", TRUE)->setBoolValue(mBackgroundVisible);
node->createChild("border_visible", TRUE)->setBoolValue(mBorderVisible);
node->createChild("drop_shadow_visible", TRUE)->setBoolValue(mDropshadowVisible);
node->createChild("border_drop_shadow_visible", TRUE)->setBoolValue(mBorderDropShadowVisible);
node->createChild("h_pad", TRUE)->setIntValue(mHPad);
node->createChild("v_pad", TRUE)->setIntValue(mVPad);
// Contents
node->setStringValue(mText);
return node;
}
// static
LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
{
LLString name("text_box");
node->getAttributeString("name", name);
LLFontGL* font = LLView::selectFont(node);
LLString text = node->getTextContents();
// TomY Yes I know this is a hack, but insert a space to make a blank text field
if (text == "")
{
text = " ";
}
LLTextBox* text_box = new LLTextBox(name,
LLRect(),
text,
font,
FALSE);
LLFontGL::HAlign halign = LLView::selectFontHAlign(node);
text_box->setHAlign(halign);
text_box->initFromXML(node, parent);
if(node->hasAttribute("text_color"))
{
LLColor4 color;
LLUICtrlFactory::getAttributeColor(node, "text_color", color);
text_box->setColor(color);
}
return text_box;
}