summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-05-15 09:15:57 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-05-15 09:15:57 -0400
commit6fcc9a0eae892a8bfc1079e100a37da7f781330b (patch)
treed8c5cb3ab9aaf52cf4ffa2bed92ff66d465fa2b4 /indra/llui
parentc3da5bb12d1c7c173929433589a4068555791bea (diff)
parentbb3c36f5cbc0c3b542045fd27255eee24e03da22 (diff)
Merge branch 'main' into release/luau-scripting for Maint X release.
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/llaccordionctrl.cpp17
-rw-r--r--indra/llui/llaccordionctrl.h2
-rw-r--r--indra/llui/llbutton.cpp13
-rw-r--r--indra/llui/llbutton.h4
-rw-r--r--indra/llui/llcheckboxctrl.h2
-rw-r--r--indra/llui/llcombobox.cpp17
-rw-r--r--indra/llui/llfloater.cpp6
-rw-r--r--indra/llui/llfloater.h4
-rw-r--r--indra/llui/llfolderview.cpp29
-rw-r--r--indra/llui/llfolderview.h2
-rw-r--r--indra/llui/llfolderviewitem.cpp4
-rw-r--r--indra/llui/llfolderviewmodel.cpp4
-rw-r--r--indra/llui/llfolderviewmodel.h2
-rw-r--r--indra/llui/lllineeditor.cpp110
-rw-r--r--indra/llui/lllineeditor.h20
-rw-r--r--indra/llui/llmenugl.cpp7
-rw-r--r--indra/llui/llmodaldialog.cpp5
-rw-r--r--indra/llui/llmultisliderctrl.cpp2
-rw-r--r--indra/llui/llnotifications.cpp6
-rw-r--r--indra/llui/llnotifications.h1
-rw-r--r--indra/llui/llscrolllistcell.h2
-rw-r--r--indra/llui/llscrolllistctrl.cpp4
-rw-r--r--indra/llui/llsliderctrl.cpp2
-rw-r--r--indra/llui/lltabcontainer.cpp13
-rw-r--r--indra/llui/lltabcontainer.h4
-rw-r--r--indra/llui/lltextbase.cpp24
-rw-r--r--indra/llui/lltextbase.h18
-rw-r--r--indra/llui/lltexteditor.cpp61
-rw-r--r--indra/llui/lltexteditor.h4
-rw-r--r--indra/llui/lltextvalidate.cpp468
-rw-r--r--indra/llui/lltextvalidate.h85
-rw-r--r--indra/llui/lltimectrl.cpp41
-rw-r--r--indra/llui/lltimectrl.h26
-rw-r--r--indra/llui/lltoolbar.cpp3
-rw-r--r--indra/llui/llui.h2
-rw-r--r--indra/llui/lluictrl.cpp2
-rw-r--r--indra/llui/lluictrl.h1
-rw-r--r--indra/llui/llview.cpp28
-rw-r--r--indra/llui/llview.h4
-rw-r--r--indra/llui/llviewmodel.cpp35
-rw-r--r--indra/llui/llviewmodel.h6
41 files changed, 695 insertions, 395 deletions
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index 89969acb1b..3feb989ed5 100644
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -936,3 +936,20 @@ S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 availabl
expanded_tab_height /= num_expanded;
return expanded_tab_height;
}
+
+void LLAccordionCtrl::collapseAllTabs()
+{
+ if (mAccordionTabs.size() > 0)
+ {
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab *tab = mAccordionTabs[i];
+
+ if (tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(false);
+ }
+ }
+ arrange();
+ }
+}
diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h
index 182c8cfe04..2741db24e8 100644
--- a/indra/llui/llaccordionctrl.h
+++ b/indra/llui/llaccordionctrl.h
@@ -122,6 +122,8 @@ public:
void setComparator(const LLTabComparator* comp) { mTabComparator = comp; }
void sort();
+ void collapseAllTabs();
+
/**
* Sets filter substring as a search_term for help text when there are no any visible tabs.
*/
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 8803d751d0..16b58dcc5b 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -58,10 +58,11 @@ static LLDefaultChildRegistry::Register<LLButton> r("button");
template class LLButton* LLView::getChild<class LLButton>(
const std::string& name, BOOL recurse) const;
-// globals loaded from settings.xml
-S32 LLBUTTON_H_PAD = 0;
-S32 BTN_HEIGHT_SMALL= 0;
-S32 BTN_HEIGHT = 0;
+// globals
+S32 LLBUTTON_H_PAD = 4;
+S32 BTN_HEIGHT_SMALL= 23;
+S32 BTN_HEIGHT = 23;
+S32 BTN_DROP_SHADOW = 2;
LLButton::Params::Params()
: label_selected("label_selected"), // requires is_toggle true
@@ -92,8 +93,8 @@ LLButton::Params::Params()
image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f),
image_overlay_selected_color("image_overlay_selected_color", LLColor4::white),
flash_color("flash_color"),
- pad_right("pad_right", LLUI::getInstance()->mSettingGroups["config"]->getS32("ButtonHPad")),
- pad_left("pad_left", LLUI::getInstance()->mSettingGroups["config"]->getS32("ButtonHPad")),
+ pad_right("pad_right", LLBUTTON_H_PAD),
+ pad_left("pad_left", LLBUTTON_H_PAD),
pad_bottom("pad_bottom"),
click_callback("click_callback"),
mouse_down_callback("mouse_down_callback"),
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 7054074fd8..8ac42596e4 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -43,10 +43,10 @@
//
// PLEASE please use these "constants" when building your own buttons.
-// They are loaded from settings.xml at run time.
extern S32 LLBUTTON_H_PAD;
extern S32 BTN_HEIGHT_SMALL;
extern S32 BTN_HEIGHT;
+extern S32 BTN_DROP_SHADOW;
//
// Helpful functions
@@ -251,7 +251,7 @@ public:
void setFont(const LLFontGL *font)
{ mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); }
const LLFontGL* getFont() const { return mGLFont; }
-
+ const std::string& getText() const { return getCurrentLabel().getString(); }
S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; }
bool labelIsTruncated() const;
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
index 43e887fab6..f2b61db308 100644
--- a/indra/llui/llcheckboxctrl.h
+++ b/indra/llui/llcheckboxctrl.h
@@ -117,7 +117,7 @@ public:
std::string getLabel() const;
void setFont( const LLFontGL* font ) { mFont = font; }
- const LLFontGL* getFont() { return mFont; }
+ const LLFontGL* getFont() const { return mFont; }
virtual void setControlName(const std::string& control_name, LLView* context);
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 1573ccb0d7..71dd93d07d 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -188,6 +188,8 @@ LLComboBox::~LLComboBox()
// explicitly disconect this signal, since base class destructor might fire top lost
mTopLostSignalConnection.disconnect();
mImageLoadedConnection.disconnect();
+
+ LLUI::getInstance()->removePopup(this);
}
@@ -482,8 +484,6 @@ void LLComboBox::onFocusLost()
void LLComboBox::setButtonVisible(BOOL visible)
{
- static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
-
mButton->setVisible(visible);
if (mTextEntry)
{
@@ -491,7 +491,7 @@ void LLComboBox::setButtonVisible(BOOL visible)
if (visible)
{
S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
- text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW;
}
//mTextEntry->setRect(text_entry_rect);
mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
@@ -530,19 +530,18 @@ void LLComboBox::setEnabledByValue(const LLSD& value, BOOL enabled)
void LLComboBox::createLineEditor(const LLComboBox::Params& p)
{
- static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
LLRect rect = getLocalRect();
if (mAllowTextEntry)
{
S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
- S32 shadow_size = drop_shadow_button;
+ S32 shadow_size = BTN_DROP_SHADOW;
mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size,
rect.mTop, rect.mRight, rect.mBottom));
mButton->setTabStop(FALSE);
mButton->setHAlign(LLFontGL::HCENTER);
LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW;
// clear label on button
std::string cur_label = mButton->getLabelSelected();
LLLineEditor::Params params = p.combo_editor;
@@ -1081,13 +1080,11 @@ void LLComboBox::onSetHighlight() const
void LLComboBox::imageLoaded()
{
- static LLUICachedControl<S32> drop_shadow_button("DropShadowButton", 0);
-
if (mAllowTextEntry)
{
LLRect rect = getLocalRect();
S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
- S32 shadow_size = drop_shadow_button;
+ S32 shadow_size = BTN_DROP_SHADOW;
mButton->setRect(LLRect(getRect().getWidth() - llmax(8, arrow_width) - 2 * shadow_size,
rect.mTop, rect.mRight, rect.mBottom));
if (mButton->getVisible())
@@ -1096,7 +1093,7 @@ void LLComboBox::imageLoaded()
if (mTextEntry)
{
LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * drop_shadow_button;
+ text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * BTN_DROP_SHADOW;
mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
}
}
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 96d9e31f4c..75254f80d8 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -2039,10 +2039,9 @@ void LLFloater::drawShadow(LLPanel* panel)
S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH;
S32 bottom = LLPANEL_BORDER_WIDTH;
- static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0);
static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow");
LLColor4 shadow_color = shadow_color_cached;
- F32 shadow_offset = (F32)shadow_offset_S32;
+ F32 shadow_offset = (F32)DROP_SHADOW_FLOATER;
if (!panel->isBackgroundOpaque())
{
@@ -2477,7 +2476,8 @@ void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
void LLFloaterView::restoreAll()
{
// make sure all subwindows aren't minimized
- for (auto child : *getChildList())
+ child_list_t child_list = *(getChildList());
+ for (LLView* child : child_list)
{
LLFloater* floaterp = dynamic_cast<LLFloater*>(child);
if (floaterp)
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 7fcbd5c5ec..dcc9af4c6e 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -61,6 +61,10 @@ const BOOL CLOSE_NO = FALSE;
const BOOL ADJUST_VERTICAL_YES = TRUE;
const BOOL ADJUST_VERTICAL_NO = FALSE;
+const F32 CONTEXT_CONE_IN_ALPHA = 0.f;
+const F32 CONTEXT_CONE_OUT_ALPHA = 1.f;
+const F32 CONTEXT_CONE_FADE_TIME = .08f;
+
namespace LLFloaterEnums
{
enum EOpenPositioning
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index fa6128cc25..33921cf4f0 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -220,7 +220,7 @@ LLFolderView::LLFolderView(const Params& p)
params.font(getLabelFontForStyle(LLFontGL::NORMAL));
params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN);
params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2));
- params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe);
+ params.prevalidator(&LLTextValidate::validateASCIIPrintableNoPipe);
params.commit_on_focus_lost(true);
params.visible(false);
mRenamer = LLUICtrlFactory::create<LLLineEditor> (params);
@@ -256,7 +256,13 @@ LLFolderView::LLFolderView(const Params& p)
// Destroys the object
LLFolderView::~LLFolderView( void )
{
- closeRenamer();
+ mRenamerTopLostSignalConnection.disconnect();
+ if (mRenamer)
+ {
+ // instead of using closeRenamer remove it directly,
+ // since it might already be hidden
+ LLUI::getInstance()->removePopup(mRenamer);
+ }
// The release focus call can potentially call the
// scrollcontainer, which can potentially be called with a partly
@@ -335,9 +341,9 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height )
void LLFolderView::filter( LLFolderViewFilter& filter )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- static LLCachedControl<S32> time_visible(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameVisible", 10);
- static LLCachedControl<S32> time_invisible(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameUnvisible", 1);
- filter.resetTime(llclamp((mParentPanel.get()->getVisible() ? time_visible() : time_invisible()), 1, 100));
+ const S32 TIME_VISIBLE = 10; // in milliseconds
+ const S32 TIME_INVISIBLE = 1;
+ filter.resetTime(llclamp((mParentPanel.get()->getVisible() ? TIME_VISIBLE : TIME_INVISIBLE), 1, 100));
// Note: we filter the model, not the view
getViewModelItem()->filter(filter);
@@ -765,7 +771,7 @@ void LLFolderView::removeSelectedItems()
}
else
{
- LL_INFOS() << "Cannot delete " << item->getName() << LL_ENDL;
+ LL_DEBUGS() << "Cannot delete " << item->getName() << LL_ENDL;
return;
}
}
@@ -1072,7 +1078,10 @@ void LLFolderView::startRenamingSelectedItem( void )
mRenamer->setVisible( TRUE );
// set focus will fail unless item is visible
mRenamer->setFocus( TRUE );
- mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
+ if (!mRenamerTopLostSignalConnection.connected())
+ {
+ mRenamerTopLostSignalConnection = mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
+ }
LLUI::getInstance()->addPopup(mRenamer);
}
}
@@ -1598,7 +1607,11 @@ BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
void LLFolderView::deleteAllChildren()
{
- closeRenamer();
+ mRenamerTopLostSignalConnection.disconnect();
+ if (mRenamer)
+ {
+ LLUI::getInstance()->removePopup(mRenamer);
+ }
if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
mPopupMenuHandle.markDead();
mScrollContainer = NULL;
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
index 1ad7e9f58c..ca78bd3072 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -345,6 +345,8 @@ protected:
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar;
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar;
+ boost::signals2::connection mRenamerTopLostSignalConnection;
+
bool mForceArrange;
public:
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 40bb6832d4..a9e1171444 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -605,15 +605,13 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
{
- static LLCachedControl<S32> drag_and_drop_threshold(*LLUI::getInstance()->mSettingGroups["config"],"DragAndDropDistanceThreshold", 3);
-
mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
if( hasMouseCapture() && isMovable() )
{
LLFolderView* root = getRoot();
- if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold()
+ if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD
&& root->getAllowDrag()
&& root->getCurSelectedItem()
&& root->startDrag())
diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp
index c7ed65bdfa..d9e6567cd6 100644
--- a/indra/llui/llfolderviewmodel.cpp
+++ b/indra/llui/llfolderviewmodel.cpp
@@ -48,8 +48,8 @@ std::string LLFolderViewModelCommon::getStatusText(bool is_empty_folder)
void LLFolderViewModelCommon::filter()
{
- static LLCachedControl<S32> max_time(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameVisible", 10);
- getFilter().resetTime(llclamp(max_time(), 1, 100));
+ const S32 MAX_FILTER_TIME = 10;
+ getFilter().resetTime(MAX_FILTER_TIME);
mFolderView->getViewModelItem()->filter(getFilter());
}
diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h
index 53ae2bd97f..f02c1e3883 100644
--- a/indra/llui/llfolderviewmodel.h
+++ b/indra/llui/llfolderviewmodel.h
@@ -170,7 +170,7 @@ public:
virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder
virtual void move( LLFolderViewModelItem* parent_listener ) = 0;
- virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed
+ virtual BOOL isItemRemovable( bool check_worn = true ) const = 0; // Can be destroyed
virtual BOOL removeItem() = 0;
virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0;
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index eefba0d186..6dc68b4de2 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -83,8 +83,8 @@ template class LLLineEditor* LLView::getChild<class LLLineEditor>(
LLLineEditor::Params::Params()
: max_length(""),
keystroke_callback("keystroke_callback"),
- prevalidate_callback("prevalidate_callback"),
- prevalidate_input_callback("prevalidate_input_callback"),
+ prevalidator("prevalidator"),
+ input_prevalidator("input_prevalidator"),
background_image("background_image"),
background_image_disabled("background_image_disabled"),
background_image_focused("background_image_focused"),
@@ -96,6 +96,7 @@ LLLineEditor::Params::Params()
commit_on_focus_lost("commit_on_focus_lost", true),
ignore_tab("ignore_tab", true),
is_password("is_password", false),
+ allow_emoji("allow_emoji", true),
cursor_color("cursor_color"),
use_bg_color("use_bg_color", false),
bg_color("bg_color"),
@@ -111,6 +112,8 @@ LLLineEditor::Params::Params()
default_text("default_text")
{
changeDefault(mouse_opaque, true);
+ addSynonym(prevalidator, "prevalidate_callback");
+ addSynonym(input_prevalidator, "prevalidate_input_callback");
addSynonym(select_on_focus, "select_all_on_focus_received");
addSynonym(border, "border");
addSynonym(label, "watermark_text");
@@ -142,6 +145,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mIgnoreArrowKeys( FALSE ),
mIgnoreTab( p.ignore_tab ),
mDrawAsterixes( p.is_password ),
+ mAllowEmoji( p.allow_emoji ),
mSpellCheck( p.spellcheck ),
mSpellCheckStart(-1),
mSpellCheckEnd(-1),
@@ -157,6 +161,8 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mUseBgColor(p.use_bg_color),
mHaveHistory(FALSE),
mReplaceNewlinesWithSpaces( TRUE ),
+ mPrevalidator(p.prevalidator()),
+ mInputPrevalidator(p.input_prevalidator()),
mLabel(p.label),
mCursorColor(p.cursor_color()),
mBgColor(p.bg_color()),
@@ -210,8 +216,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
}
mSpellCheckTimer.reset();
- setPrevalidateInput(p.prevalidate_input_callback());
- setPrevalidate(p.prevalidate_callback());
+ updateAllowingLanguageInput();
}
LLLineEditor::~LLLineEditor()
@@ -416,6 +421,11 @@ void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit
all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived);
std::string truncated_utf8 = new_text;
+ if (!mAllowEmoji)
+ {
+ // Cut emoji symbols if exist
+ utf8str_remove_emojis(truncated_utf8);
+ }
if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes)
{
truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes);
@@ -589,13 +599,21 @@ void LLLineEditor::replaceWithSuggestion(U32 index)
{
if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
{
+ LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
+ if (!mAllowEmoji)
+ {
+ // Cut emoji symbols if exist
+ wstring_remove_emojis(suggestion);
+ }
+ if (suggestion.empty())
+ return;
+
deselect();
// Delete the misspelled word
mText.erase(it->first, it->second - it->first);
// Insert the suggestion in its place
- LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
mText.insert(it->first, suggestion);
setCursor(it->first + (S32)suggestion.length());
@@ -958,9 +976,11 @@ void LLLineEditor::removeChar()
}
}
-
void LLLineEditor::addChar(const llwchar uni_char)
{
+ if (!mAllowEmoji && LLStringOps::isEmoji(uni_char))
+ return;
+
llwchar new_c = uni_char;
if (hasSelection())
{
@@ -1193,11 +1213,12 @@ void LLLineEditor::cut()
deleteSelection();
// Validate new string and rollback the if needed.
- BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
- if( need_to_rollback )
+ BOOL need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
{
rollback.doRollback( this );
LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
}
else
{
@@ -1260,6 +1281,11 @@ void LLLineEditor::pasteHelper(bool is_primary)
if (!paste.empty())
{
+ if (!mAllowEmoji)
+ {
+ wstring_remove_emojis(paste);
+ }
+
if (!prevalidateInput(paste))
return;
@@ -1321,11 +1347,12 @@ void LLLineEditor::pasteHelper(bool is_primary)
deselect();
// Validate new string and rollback the if needed.
- BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
- if( need_to_rollback )
+ BOOL need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
{
rollback.doRollback( this );
LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
}
else
{
@@ -1568,19 +1595,27 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
deselect();
}
- BOOL need_to_rollback = FALSE;
+ bool prevalidator_failed = false;
// If read-only, don't allow changes
- need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText()));
+ bool need_to_rollback = mReadOnly && (mText.getString() == rollback.getText());
// Validate new string and rollback the keystroke if needed.
- need_to_rollback |= (mPrevalidateFunc && !mPrevalidateFunc(mText.getWString()));
+ if (!need_to_rollback && mPrevalidator)
+ {
+ prevalidator_failed = !mPrevalidator.validate(mText.getWString());
+ need_to_rollback |= prevalidator_failed;
+ }
if (need_to_rollback)
{
rollback.doRollback(this);
LLUI::getInstance()->reportBadKeystroke();
+ if (prevalidator_failed)
+ {
+ mPrevalidator.showLastErrorUsingTimeout();
+ }
}
// Notify owner if requested
@@ -1627,20 +1662,18 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
deselect();
- BOOL need_to_rollback = FALSE;
-
// Validate new string and rollback the keystroke if needed.
- need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
-
- if( need_to_rollback )
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
{
rollback.doRollback( this );
LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
}
// Notify owner if requested
- if( !need_to_rollback && handled )
+ if (!need_to_rollback && handled)
{
// HACK! The only usage of this callback doesn't do anything with the character.
// We'll have to do something about this if something ever changes! - Doug
@@ -1683,11 +1716,12 @@ void LLLineEditor::doDelete()
}
// Validate new string and rollback the if needed.
- BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
- if( need_to_rollback )
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
{
- rollback.doRollback( this );
+ rollback.doRollback(this);
LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
}
else
{
@@ -1740,19 +1774,6 @@ void LLLineEditor::drawBackground()
}
//virtual
-const std::string LLLineEditor::getToolTip() const
-{
- if (sDebugUnicode)
- {
- std::string text = getText();
- std::string tooltip = utf8str_showBytesUTF8(text);
- return tooltip;
- }
-
- return LLUICtrl::getToolTip();
-}
-
-//virtual
void LLLineEditor::draw()
{
F32 alpha = getDrawContext().mAlpha;
@@ -2232,7 +2253,7 @@ void LLLineEditor::setFocus( BOOL new_state )
// fine on 1.15.0.2, since all prevalidate func reject any
// non-ASCII characters. I'm not sure on future versions,
// however.
- getWindow()->allowLanguageTextInput(this, mPrevalidateFunc == NULL);
+ getWindow()->allowLanguageTextInput(this, !mPrevalidator);
}
}
@@ -2251,26 +2272,21 @@ void LLLineEditor::setRect(const LLRect& rect)
}
}
-void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
+void LLLineEditor::setPrevalidate(LLTextValidate::Validator validator)
{
- mPrevalidateFunc = func;
+ mPrevalidator = validator;
updateAllowingLanguageInput();
}
-void LLLineEditor::setPrevalidateInput(LLTextValidate::validate_func_t func)
+void LLLineEditor::setPrevalidateInput(LLTextValidate::Validator validator)
{
- mPrevalidateInputFunc = func;
+ mInputPrevalidator = validator;
updateAllowingLanguageInput();
}
bool LLLineEditor::prevalidateInput(const LLWString& wstr)
{
- if (mPrevalidateInputFunc && !mPrevalidateInputFunc(wstr))
- {
- return false;
- }
-
- return true;
+ return mInputPrevalidator.validate(wstr);
}
// static
@@ -2412,7 +2428,7 @@ void LLLineEditor::updateAllowingLanguageInput()
// test app, no window available
return;
}
- if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL)
+ if (hasFocus() && !mReadOnly && !mDrawAsterixes && !mPrevalidator)
{
window->allowLanguageTextInput(this, TRUE);
}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 65afa54d04..340308535f 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -76,8 +76,8 @@ public:
Optional<MaxLength> max_length;
Optional<keystroke_callback_t> keystroke_callback;
- Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
- Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_input_callback;
+ Optional<LLTextValidate::Validator, LLTextValidate::Validators> prevalidator;
+ Optional<LLTextValidate::Validator, LLTextValidate::Validators> input_prevalidator;
Optional<LLViewBorder::Params> border;
@@ -93,6 +93,7 @@ public:
bg_image_always_focused,
show_label_focused,
is_password,
+ allow_emoji,
use_bg_color;
// colors
@@ -175,7 +176,6 @@ public:
void onSpellCheckSettingsChange();
// view overrides
- /*virtual*/ const std::string getToolTip() const override;
/*virtual*/ void draw() override;
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override;
/*virtual*/ void onFocusReceived() override;
@@ -203,7 +203,7 @@ public:
void setText(const LLStringExplicit &new_text);
- const std::string& getText() const { return mText.getString(); }
+ const std::string& getText() const override { return mText.getString(); }
LLWString getWText() const { return mText.getWString(); }
LLWString getConvertedText() const; // trimmed text with paragraphs converted to newlines
@@ -235,12 +235,13 @@ public:
const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); }
const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); }
- const LLFontGL* getFont() const { return mGLFont; }
+ const LLFontGL* getFont() const override { return mGLFont; }
void setFont(const LLFontGL* font);
void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; }
void setIgnoreTab(BOOL b) { mIgnoreTab = b; }
void setPassDelete(BOOL b) { mPassDelete = b; }
+ void setAllowEmoji(BOOL b) { mAllowEmoji = b; }
void setDrawAsterixes(BOOL b);
// get the cursor position of the beginning/end of the prev/next word in the text
@@ -267,12 +268,12 @@ public:
void setTextPadding(S32 left, S32 right);
// Prevalidation controls which keystrokes can affect the editor
- void setPrevalidate( LLTextValidate::validate_func_t func );
+ void setPrevalidate(LLTextValidate::Validator validator);
// This method sets callback that prevents from:
// - deleting, selecting, typing, cutting, pasting characters that are not valid.
// Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed
// symbols, before existing text is modified, but setPrevalidate validates line after it was modified.
- void setPrevalidateInput(LLTextValidate::validate_func_t func);
+ void setPrevalidateInput(LLTextValidate::Validator validator);
static BOOL postvalidateFloat(const std::string &str);
bool prevalidateInput(const LLWString& wstr);
@@ -374,8 +375,8 @@ protected:
std::list<std::pair<U32, U32> > mMisspellRanges;
std::vector<std::string> mSuggestionList;
- LLTextValidate::validate_func_t mPrevalidateFunc;
- LLTextValidate::validate_func_t mPrevalidateInputFunc;
+ LLTextValidate::Validator mPrevalidator;
+ LLTextValidate::Validator mInputPrevalidator;
LLFrameTimer mKeystrokeTimer;
LLTimer mTripleClickTimer;
@@ -403,6 +404,7 @@ protected:
BOOL mShowImageFocused;
BOOL mShowLabelFocused;
+ bool mAllowEmoji;
bool mUseBgColor;
LLWString mPreeditWString;
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 79d0fbd1b4..8fcb558aae 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -65,8 +65,8 @@
LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
view_listener_t::listener_map_t view_listener_t::sListeners;
-S32 MENU_BAR_HEIGHT = 0;
-S32 MENU_BAR_WIDTH = 0;
+S32 MENU_BAR_HEIGHT = 18;
+S32 MENU_BAR_WIDTH = 410;
///============================================================================
/// Local function declarations, constants, enums, and typedefs
@@ -3234,10 +3234,9 @@ void LLMenuGL::draw( void )
}
if (mDropShadowed && !mTornOff)
{
- static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0);
static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- color_drop_shadow, drop_shadow_floater );
+ color_drop_shadow, DROP_SHADOW_FLOATER);
}
if( mBgVisible )
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
index b2be33b266..eac43900f6 100644
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -67,6 +67,8 @@ LLModalDialog::~LLModalDialog()
{
LL_ERRS() << "Attempt to delete dialog while still in sModalStack!" << LL_ENDL;
}
+
+ LLUI::getInstance()->removePopup(this);
}
// virtual
@@ -284,10 +286,9 @@ BOOL LLModalDialog::handleKeyHere(KEY key, MASK mask )
void LLModalDialog::draw()
{
static LLUIColor shadow_color = LLUIColorTable::instance().getColor("ColorDropShadow");
- static LLUICachedControl<S32> shadow_lines ("DropShadowFloater", 0);
gl_drop_shadow( 0, getRect().getHeight(), getRect().getWidth(), 0,
- shadow_color, shadow_lines);
+ shadow_color, DROP_SHADOW_FLOATER);
LLFloater::draw();
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index 5319b0d3b6..b0598b4388 100644
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -138,7 +138,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
params.font(p.font);
params.max_length.bytes(MAX_STRING_LENGTH);
params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit);
- params.prevalidate_callback(&LLTextValidate::validateFloat);
+ params.prevalidator(&LLTextValidate::validateFloat);
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) );
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 875be5bc6f..2fbae73b65 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -87,6 +87,7 @@ LLNotificationForm::FormInput::FormInput()
: type("type"),
text("text"),
max_length_chars("max_length_chars"),
+ allow_emoji("allow_emoji"),
width("width", 0),
value("value")
{}
@@ -1551,6 +1552,11 @@ bool LLNotifications::loadTemplates()
// specific skin.
std::vector<std::string> search_paths =
gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS);
+ if (search_paths.empty())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem finding notifications.xml" << LL_ENDL;
+ }
std::string base_filename = search_paths.front();
LLXMLNodePtr root;
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 99f67c06e5..ab4f009a80 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -201,6 +201,7 @@ public:
Mandatory<std::string> type;
Optional<S32> width;
Optional<S32> max_length_chars;
+ Optional<bool> allow_emoji;
Optional<std::string> text;
Optional<std::string> value;
diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h
index 0073ac4ead..ea2327961a 100644
--- a/indra/llui/llscrolllistcell.h
+++ b/indra/llui/llscrolllistcell.h
@@ -81,7 +81,7 @@ public:
alt_value("alt_value", ""),
label("label"),
tool_tip("tool_tip", ""),
- font("font", LLFontGL::getFontSansSerifSmall()),
+ font("font", LLFontGL::getFontEmojiSmall()),
font_color("font_color", LLColor4::black),
color("color", LLColor4::white),
font_halign("halign", LLFontGL::LEFT)
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 5b9944d3e0..7fb732eca3 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -1810,7 +1810,7 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
// display tooltip exactly over original cell, in same font
LLToolTipMgr::instance().show(LLToolTip::Params()
.message(hit_cell->getToolTip())
- .font(LLFontGL::getFontSansSerifSmall())
+ .font(LLFontGL::getFontEmojiSmall())
.pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6))
.delay_time(0.2f)
.sticky_rect(sticky_rect));
@@ -3294,7 +3294,7 @@ LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, E
item_params.value(entry_id);
item_params.columns.add()
.value(value)
- .font(LLFontGL::getFontSansSerifSmall());
+ .font(LLFontGL::getFontEmojiSmall());
return addRow(item_params, pos);
}
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index 53eb667a1d..e16ba9408e 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -167,7 +167,7 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
}
line_p.commit_callback.function(&LLSliderCtrl::onEditorCommit);
- line_p.prevalidate_callback(&LLTextValidate::validateFloat);
+ line_p.prevalidator(&LLTextValidate::validateFloat);
mEditor = LLUICtrlFactory::create<LLLineEditor>(line_p);
mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this ));
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 46f60508b2..78c4f3c03c 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -225,7 +225,8 @@ LLTabContainer::Params::Params()
tabs_flashing_color("tabs_flashing_color"),
tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
use_ellipses("use_ellipses"),
- font_halign("halign")
+ font_halign("halign"),
+ use_tab_offset("use_tab_offset", false)
{}
LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
@@ -264,7 +265,8 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
mTabIconCtrlPad(p.tab_icon_ctrl_pad),
mEnableTabsFlashing(p.enable_tabs_flashing),
mTabsFlashingColor(p.tabs_flashing_color),
- mUseTabEllipses(p.use_ellipses)
+ mUseTabEllipses(p.use_ellipses),
+ mUseTabOffset(p.use_tab_offset)
{
static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
@@ -1023,10 +1025,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
}
else
{
- tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH * 3,
- tab_panel_top,
- getRect().getWidth() - LLPANEL_BORDER_WIDTH * 2,
- tab_panel_bottom );
+ S32 left_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 3 : LLPANEL_BORDER_WIDTH;
+ S32 right_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 2 : LLPANEL_BORDER_WIDTH;
+ tab_panel_rect = LLRect(left_offset, tab_panel_top, getRect().getWidth() - right_offset, tab_panel_bottom);
}
child->setFollowsAll();
child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 0604785e7a..626255be8c 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -121,6 +121,8 @@ public:
*/
Optional<S32> tab_icon_ctrl_pad;
+ Optional<bool> use_tab_offset;
+
Params();
};
@@ -321,6 +323,8 @@ private:
S32 mTabIconCtrlPad;
bool mUseTabEllipses;
LLFrameTimer mMouseDownTimer;
+
+ bool mUseTabOffset;
};
#endif // LL_TABCONTAINER_H
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 1460bb85b6..c1eedf93a7 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -168,6 +168,7 @@ LLTextBase::Params::Params()
trusted_content("trusted_content", true),
always_show_icons("always_show_icons", false),
use_ellipses("use_ellipses", false),
+ use_emoji("use_emoji", true),
use_color("use_color", true),
parse_urls("parse_urls", false),
force_urls_external("force_urls_external", false),
@@ -227,6 +228,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mPlainText ( p.plain_text ),
mWordWrap(p.wrap),
mUseEllipses( p.use_ellipses ),
+ mUseEmoji(p.use_emoji),
mUseColor(p.use_color),
mParseHTML(p.parse_urls),
mForceUrlsExternal(p.force_urls_external),
@@ -903,6 +905,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
}
// Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us)
+ if (mUseEmoji)
{
LLStyleSP emoji_style;
LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
@@ -915,7 +918,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
if (!emoji_style)
{
emoji_style = new LLStyle(getStyleParams());
- emoji_style->setFont(LLFontGL::getFontEmoji());
+ emoji_style->setFont(LLFontGL::getFontEmojiLarge());
}
S32 new_seg_start = pos + text_kitty;
@@ -1287,19 +1290,6 @@ BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
}
//virtual
-const std::string LLTextBase::getToolTip() const
-{
- if (sDebugUnicode)
- {
- std::string text = getText();
- std::string tooltip = utf8str_showBytesUTF8(text);
- return tooltip;
- }
-
- return LLUICtrl::getToolTip();
-}
-
-//virtual
void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
{
if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape)
@@ -2182,10 +2172,10 @@ void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params&
onValueChange(0, getLength());
}
-//virtual
-std::string LLTextBase::getText() const
+// virtual
+const std::string& LLTextBase::getText() const
{
- return getViewModel()->getValue().asString();
+ return getViewModel()->getStringValue();
}
// IDEVO - icons can be UI image names or UUID sent from
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 13fb1ff397..97507fe800 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -328,6 +328,7 @@ public:
plain_text,
wrap,
use_ellipses,
+ use_emoji,
use_color,
parse_urls,
force_urls_external,
@@ -366,7 +367,6 @@ public:
/*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask) override;
// LLView interface
- /*virtual*/ const std::string getToolTip() const override;
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override;
/*virtual*/ void draw() override;
@@ -408,12 +408,15 @@ public:
virtual void onSpellCheckPerformed(){}
// used by LLTextSegment layout code
- bool getWordWrap() { return mWordWrap; }
- bool getUseEllipses() { return mUseEllipses; }
- bool getUseColor() { return mUseColor; }
+ bool getWordWrap() const { return mWordWrap; }
+ bool getUseEllipses() const { return mUseEllipses; }
+ bool getUseEmoji() const { return mUseEmoji; }
+ void setUseEmoji(bool value) { mUseEmoji = value; }
+ bool getUseColor() const { return mUseColor; }
+ void setUseColor(bool value) { mUseColor = value; }
bool truncate(); // returns true of truncation occurred
- bool isContentTrusted() {return mTrustedContent;}
+ bool isContentTrusted() const { return mTrustedContent; }
void setContentTrusted(bool trusted_content) { mTrustedContent = trusted_content; }
// TODO: move into LLTextSegment?
@@ -422,7 +425,7 @@ public:
// Text accessors
// TODO: add optional style parameter
virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style
- virtual std::string getText() const;
+ /*virtual*/ const std::string& getText() const override;
void setMaxTextLength(S32 length) { mMaxTextByteLength = length; }
S32 getMaxTextLength() { return mMaxTextByteLength; }
@@ -495,7 +498,7 @@ public:
bool scrolledToStart();
bool scrolledToEnd();
- const LLFontGL* getFont() const { return mFont; }
+ const LLFontGL* getFont() const override { return mFont; }
virtual void appendLineBreakSegment(const LLStyle::Params& style_params);
virtual void appendImageSegment(const LLStyle::Params& style_params);
@@ -716,6 +719,7 @@ protected:
bool mParseHighlights; // highlight user-defined keywords
bool mWordWrap;
bool mUseEllipses;
+ bool mUseEmoji;
bool mUseColor;
bool mTrackEnd; // if true, keeps scroll position at end of document during resize
bool mReadOnly;
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 348a4603de..5f5f47c45c 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -232,7 +232,7 @@ private:
///////////////////////////////////////////////////////////////////
LLTextEditor::Params::Params()
: default_text("default_text"),
- prevalidate_callback("prevalidate_callback"),
+ prevalidator("prevalidator"),
embedded_items("embedded_items", false),
ignore_tab("ignore_tab", true),
auto_indent("auto_indent", true),
@@ -242,7 +242,8 @@ LLTextEditor::Params::Params()
show_emoji_helper("show_emoji_helper"),
enable_tooltip_paste("enable_tooltip_paste")
{
- addSynonym(prevalidate_callback, "text_type");
+ addSynonym(prevalidator, "prevalidate_callback");
+ addSynonym(prevalidator, "text_type");
}
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
@@ -253,16 +254,17 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mLastCmd( NULL ),
mDefaultColor( p.default_color() ),
mAutoIndent(p.auto_indent),
+ mParseOnTheFly(false),
mCommitOnFocusLost( p.commit_on_focus_lost),
mAllowEmbeddedItems( p.embedded_items ),
mMouseDownX(0),
mMouseDownY(0),
mTabsToNextField(p.ignore_tab),
- mPrevalidateFunc(p.prevalidate_callback()),
+ mPrevalidator(p.prevalidator()),
mShowContextMenu(p.show_context_menu),
mShowEmojiHelper(p.show_emoji_helper),
mEnableTooltipPaste(p.enable_tooltip_paste),
- mPassDelete(FALSE),
+ mPassDelete(false),
mKeepSelectionOnReturn(false)
{
mSourceID.generate();
@@ -278,7 +280,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
addChild( mBorder );
setText(p.default_text());
- mParseOnTheFly = TRUE;
+ mParseOnTheFly = true;
}
void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
@@ -319,11 +321,13 @@ LLTextEditor::~LLTextEditor()
void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
{
// validate incoming text if necessary
- if (mPrevalidateFunc)
+ if (mPrevalidator)
{
- LLWString test_text = utf8str_to_wstring(utf8str);
- if (!mPrevalidateFunc(test_text))
+ if (!mPrevalidator.validate(utf8str))
{
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+
// not valid text, nothing to do
return;
}
@@ -332,9 +336,9 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param
blockUndo();
deselect();
- mParseOnTheFly = FALSE;
+ mParseOnTheFly = false;
LLTextBase::setText(utf8str, input_params);
- mParseOnTheFly = TRUE;
+ mParseOnTheFly = true;
resetDirty();
}
@@ -609,7 +613,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
// Disabling parsing on the fly to avoid updating text segments
// until all indentation commands are executed.
- mParseOnTheFly = FALSE;
+ mParseOnTheFly = false;
// Find each start-of-line and indent it
do
@@ -636,7 +640,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
while( cur < right );
- mParseOnTheFly = TRUE;
+ mParseOnTheFly = true;
if( (right < getLength()) && (text[right] == '\n') )
{
@@ -685,7 +689,7 @@ void LLTextEditor::insertEmoji(llwchar emoji)
{
LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL;
auto styleParams = LLStyle::Params();
- styleParams.font = LLFontGL::getFontEmoji();
+ styleParams.font = LLFontGL::getFontEmojiLarge();
auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this);
insert(mCursorPos, LLWString(1, emoji), false, segment);
setCursorPos(mCursorPos + 1);
@@ -986,10 +990,12 @@ S32 LLTextEditor::execute( TextCmd* cmd )
mUndoStack.push_front(cmd);
mLastCmd = cmd;
- bool need_to_rollback = mPrevalidateFunc
- && !mPrevalidateFunc(getViewModel()->getDisplay());
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(getViewModel()->getDisplay());
if (need_to_rollback)
{
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+
// get rid of this last command and clean up undo stack
undo();
@@ -1127,14 +1133,13 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc)
{
if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc )) > mMaxTextByteLength)
{
- make_ui_sound("UISndBadKeystroke");
+ LLUI::getInstance()->reportBadKeystroke();
return 0;
}
if (mLastCmd && mLastCmd->canExtend(pos))
{
- S32 delta = 0;
- if (mPrevalidateFunc)
+ if (mPrevalidator)
{
// get a copy of current text contents
LLWString test_string(getViewModel()->getDisplay());
@@ -1142,20 +1147,22 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc)
// modify text contents as if this addChar succeeded
llassert(pos <= (S32)test_string.size());
test_string.insert(pos, 1, wc);
- if (!mPrevalidateFunc( test_string))
+ if (!mPrevalidator.validate(test_string))
{
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
return 0;
}
}
+
+ S32 delta = 0;
mLastCmd->extendAndExecute(this, pos, wc, &delta);
return delta;
}
- else
- {
+
return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));
}
-}
void LLTextEditor::addChar(llwchar wc)
{
@@ -1163,6 +1170,7 @@ void LLTextEditor::addChar(llwchar wc)
{
return;
}
+
if( hasSelection() )
{
deleteSelection(TRUE);
@@ -1508,7 +1516,13 @@ void LLTextEditor::pastePrimary()
// paste from primary (itsprimary==true) or clipboard (itsprimary==false)
void LLTextEditor::pasteHelper(bool is_primary)
{
- mParseOnTheFly = FALSE;
+ struct BoolReset
+ {
+ BoolReset(bool& value) : mValuePtr(&value) { *mValuePtr = false; }
+ ~BoolReset() { *mValuePtr = true; }
+ bool* mValuePtr;
+ } reset(mParseOnTheFly);
+
bool can_paste_it;
if (is_primary)
{
@@ -1550,7 +1564,6 @@ void LLTextEditor::pasteHelper(bool is_primary)
deselect();
onKeyStroke();
- mParseOnTheFly = TRUE;
}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 2bc4184ca8..0963c83d28 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -55,7 +55,7 @@ public:
struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
{
Optional<std::string> default_text;
- Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
+ Optional<LLTextValidate::Validator, LLTextValidate::Validators> prevalidator;
Optional<bool> embedded_items,
ignore_tab,
@@ -349,7 +349,7 @@ private:
LLCoordGL mLastIMEPosition; // Last position of the IME editor
keystroke_signal_t mKeystrokeSignal;
- LLTextValidate::validate_func_t mPrevalidateFunc;
+ LLTextValidate::Validator mPrevalidator;
LLHandle<LLContextMenu> mContextMenuHandle;
}; // end class LLTextEditor
diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp
index 07f8ac01a1..9e27ed6232 100644
--- a/indra/llui/lltextvalidate.cpp
+++ b/indra/llui/lltextvalidate.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file lltextvalidate.cpp
* @brief Text validation helper functions
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library 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
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -29,328 +29,450 @@
#include "linden_common.h"
#include "lltextvalidate.h"
+
+#include "llnotificationsutil.h"
+#include "lltrans.h"
+
#include "llresmgr.h" // for LLLocale
namespace LLTextValidate
{
- void ValidateTextNamedFuncs::declareValues()
+
+static S32 strtol(const std::string& str) { return ::strtol(str.c_str(), NULL, 10); }
+static S32 strtol(const LLWString& str) { return ::strtol(wstring_to_utf8str(str).c_str(), NULL, 10); }
+
+static LLSD llsd(const std::string& str) { return LLSD(str); }
+static LLSD llsd(const LLWString& str) { return LLSD(wstring_to_utf8str(str)); }
+template <class CHAR>
+LLSD llsd(CHAR ch) { return llsd(std::basic_string<CHAR>(1, ch)); }
+
+void ValidatorImpl::setLastErrorShowTime()
+{
+ mLastErrorShowTime = (U32Seconds)LLTimer::getTotalTime();
+}
+
+void Validator::showLastErrorUsingTimeout(U32 timeout)
+{
+ if (mImpl && (U32Seconds)LLTimer::getTotalTime() >= mImpl->getLastErrorShowTime() + timeout)
{
- declare("ascii", validateASCII);
- declare("float", validateFloat);
- declare("int", validateInt);
- declare("positive_s32", validatePositiveS32);
- declare("non_negative_s32", validateNonNegativeS32);
- declare("alpha_num", validateAlphaNum);
- declare("alpha_num_space", validateAlphaNumSpace);
- declare("ascii_printable_no_pipe", validateASCIIPrintableNoPipe);
- declare("ascii_printable_no_space", validateASCIIPrintableNoSpace);
- declare("ascii_with_newline", validateASCIIWithNewLine);
+ mImpl->setLastErrorShowTime();
+ std::string reason = LLTrans::getString(mImpl->getLastErrorName(), mImpl->getLastErrorValues());
+ LLNotificationsUtil::add("InvalidKeystroke", LLSD().with("REASON", reason));
}
+}
- // Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
- // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
- // the simple reasons that intermediate states may be invalid even if the final result is valid.
- //
- bool validateFloat(const LLWString &str)
+// Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
+// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+// the simple reasons that intermediate states may be invalid even if the final result is valid.
+class ValidatorFloat : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR> &str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- bool success = TRUE;
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
S32 len = trimmed.length();
- if( 0 < len )
+ if (0 < len)
{
// May be a comma or period, depending on the locale
- llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
+ CHAR decimal_point = LLResMgr::getInstance()->getDecimalPoint();
S32 i = 0;
// First character can be a negative sign
- if( '-' == trimmed[0] )
+ if ('-' == trimmed.front())
{
i++;
}
- for( ; i < len; i++ )
+ for (; i < len; i++)
{
- if( (decimal_point != trimmed[i] ) && !LLStringOps::isDigit( trimmed[i] ) )
+ CHAR ch = trimmed[i];
+ if ((decimal_point != ch) && !LLStringOps::isDigit(ch))
{
- success = FALSE;
- break;
+ return setError("Validator_ShouldBeDigitOrDot", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
}
}
}
- return success;
+ return resetError();
}
- // Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
- // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
- // the simple reasons that intermediate states may be invalid even if the final result is valid.
- //
- bool validateInt(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorFloatImpl;
+Validator validateFloat(validatorFloatImpl);
+
+// Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
+// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+// the simple reasons that intermediate states may be invalid even if the final result is valid.
+class ValidatorInt : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR> &str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- bool success = TRUE;
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
S32 len = trimmed.length();
- if( 0 < len )
+ if (0 < len)
{
S32 i = 0;
// First character can be a negative sign
- if( '-' == trimmed[0] )
+ if ('-' == trimmed.front())
{
i++;
}
- for( ; i < len; i++ )
+ for (; i < len; i++)
{
- if( !LLStringOps::isDigit( trimmed[i] ) )
+ CHAR ch = trimmed[i];
+ if (!LLStringOps::isDigit(ch))
{
- success = FALSE;
- break;
+ return setError("Validator_ShouldBeDigit", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
}
}
}
- return success;
+ return resetError();
}
- bool validatePositiveS32(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorIntImpl;
+Validator validateInt(validatorIntImpl);
+
+class ValidatorPositiveS32 : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
S32 len = trimmed.length();
- bool success = TRUE;
- if(0 < len)
+ if (0 < len)
{
- if(('-' == trimmed[0]) || ('0' == trimmed[0]))
+ CHAR ch = trimmed.front();
+
+ if (('-' == ch) || ('0' == ch))
{
- success = FALSE;
+ return setError("Validator_ShouldNotBeMinusOrZero", LLSD().with("CH", llsd(ch)));
}
- S32 i = 0;
- while(success && (i < len))
+
+ for (S32 i = 0; i < len; ++i)
{
- if(!LLStringOps::isDigit(trimmed[i++]))
+ ch = trimmed[i];
+ if (!LLStringOps::isDigit(ch))
{
- success = FALSE;
+ return setError("Validator_ShouldBeDigit", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
}
}
}
- if (success)
+
+ S32 val = strtol(trimmed);
+ if (val <= 0)
{
- S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
- if (val <= 0)
- {
- success = FALSE;
- }
+ return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(trimmed)));
}
- return success;
+
+ return resetError();
}
- bool validateNonNegativeS32(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorPositiveS32Impl;
+Validator validatePositiveS32(validatorPositiveS32Impl);
+
+class ValidatorNonNegativeS32 : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
S32 len = trimmed.length();
- bool success = TRUE;
- if(0 < len)
+ if (0 < len)
{
- if('-' == trimmed[0])
+ CHAR ch = trimmed.front();
+
+ if ('-' == ch)
{
- success = FALSE;
+ return setError("Validator_ShouldNotBeMinus", LLSD().with("CH", llsd(ch)));
}
- S32 i = 0;
- while(success && (i < len))
+
+ for (S32 i = 0; i < len; ++i)
{
- if(!LLStringOps::isDigit(trimmed[i++]))
+ ch = trimmed[i];
+ if (!LLStringOps::isDigit(ch))
{
- success = FALSE;
+ return setError("Validator_ShouldBeDigit", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
}
}
}
- if (success)
+
+ S32 val = strtol(trimmed);
+ if (val < 0)
{
- S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
- if (val < 0)
- {
- success = FALSE;
- }
+ return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(trimmed)));
}
- return success;
+
+ return resetError();
}
- bool validateNonNegativeS32NoSpace(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorNonNegativeS32Impl;
+Validator validateNonNegativeS32(validatorNonNegativeS32Impl);
+
+class ValidatorNonNegativeS32NoSpace : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- LLWString test_str = str;
+ std::basic_string<CHAR> test_str = str;
S32 len = test_str.length();
- bool success = TRUE;
- if(0 < len)
+ if (0 < len)
{
- if('-' == test_str[0])
+ CHAR ch = test_str.front();
+
+ if ('-' == ch)
{
- success = FALSE;
+ return setError("Validator_ShouldNotBeMinus", LLSD().with("CH", llsd(ch)));
}
- S32 i = 0;
- while(success && (i < len))
+
+ for (S32 i = 0; i < len; ++i)
{
- if(!LLStringOps::isDigit(test_str[i]) || LLStringOps::isSpace(test_str[i++]))
+ ch = test_str[i];
+ if (!LLStringOps::isDigit(ch) || LLStringOps::isSpace(ch))
{
- success = FALSE;
+ return setError("Validator_ShouldBeDigitNotSpace", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
}
}
}
- if (success)
+
+ S32 val = strtol(test_str);
+ if (val < 0)
{
- S32 val = strtol(wstring_to_utf8str(test_str).c_str(), NULL, 10);
- if (val < 0)
- {
- success = FALSE;
- }
+ return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(test_str)));
}
- return success;
+
+ return resetError();
}
- bool validateAlphaNum(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorNonNegativeS32NoSpaceImpl;
+Validator validateNonNegativeS32NoSpace(validatorNonNegativeS32NoSpaceImpl);
+
+class ValidatorAlphaNum : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- bool rv = TRUE;
S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
+ while (len--)
{
- if( !LLStringOps::isAlnum((char)str[len]) )
+ CHAR ch = str[len];
+
+ if (!LLStringOps::isAlnum(ch))
{
- rv = FALSE;
- break;
+ return setError("Validator_ShouldBeDigitOrAlpha", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
}
}
- return rv;
+
+ return resetError();
}
- bool validateAlphaNumSpace(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorAlphaNumImpl;
+Validator validateAlphaNum(validatorAlphaNumImpl);
+
+class ValidatorAlphaNumSpace : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
LLLocale locale(LLLocale::USER_LOCALE);
- bool rv = TRUE;
S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
+ while (len--)
{
- if(!(LLStringOps::isAlnum((char)str[len]) || (' ' == str[len])))
+ CHAR ch = str[len];
+
+ if (!LLStringOps::isAlnum(ch) && (' ' != ch))
{
- rv = FALSE;
- break;
+ return setError("Validator_ShouldBeDigitOrAlphaOrSpace", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
}
}
- return rv;
+
+ return resetError();
}
- // Used for most names of things stored on the server, due to old file-formats
- // that used the pipe (|) for multiline text storage. Examples include
- // inventory item names, parcel names, object names, etc.
- bool validateASCIIPrintableNoPipe(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorAlphaNumSpaceImpl;
+Validator validateAlphaNumSpace(validatorAlphaNumSpaceImpl);
+
+// Used for most names of things stored on the server, due to old file-formats
+// that used the pipe (|) for multiline text storage. Examples include
+// inventory item names, parcel names, object names, etc.
+class ValidatorASCIIPrintableNoPipe : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
- bool rv = TRUE;
S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
+ while (len--)
{
- llwchar wc = str[len];
- if (wc < 0x20
- || wc > 0x7f
- || wc == '|')
- {
- rv = FALSE;
- break;
- }
- if(!(wc == ' '
- || LLStringOps::isAlnum((char)wc)
- || LLStringOps::isPunct((char)wc) ) )
+ CHAR ch = str[len];
+
+ if (ch < 0x20 || ch > 0x7f || ch == '|' ||
+ (ch != ' ' && !LLStringOps::isAlnum(ch) && !LLStringOps::isPunct(ch)))
{
- rv = FALSE;
- break;
+ return setError("Validator_ShouldBeDigitOrAlphaOrPunct", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
}
}
- return rv;
+
+ return resetError();
}
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIPrintableNoPipeImpl;
+Validator validateASCIIPrintableNoPipe(validatorASCIIPrintableNoPipeImpl);
- // Used for avatar names
- bool validateASCIIPrintableNoSpace(const LLWString &str)
+// Used for avatar names
+class ValidatorASCIIPrintableNoSpace : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
- bool rv = TRUE;
S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
+ while (len--)
{
- llwchar wc = str[len];
- if (wc < 0x20
- || wc > 0x7f
- || LLStringOps::isSpace(wc))
- {
- rv = FALSE;
- break;
- }
- if( !(LLStringOps::isAlnum((char)str[len]) ||
- LLStringOps::isPunct((char)str[len]) ) )
+ CHAR ch = str[len];
+
+ if (ch <= 0x20 || ch > 0x7f || LLStringOps::isSpace(ch) ||
+ (!LLStringOps::isAlnum(ch) && !LLStringOps::isPunct(ch)))
{
- rv = FALSE;
- break;
+ return setError("Validator_ShouldBeDigitOrAlphaOrPunctNotSpace", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
}
}
- return rv;
+
+ return resetError();
}
- bool validateASCII(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIPrintableNoSpaceImpl;
+Validator validateASCIIPrintableNoSpace(validatorASCIIPrintableNoSpaceImpl);
+
+class ValidatorASCII : public ValidatorImpl
+{
+protected:
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
- bool rv = TRUE;
S32 len = str.length();
- while(len--)
+ while (len--)
{
- if (str[len] < 0x20 || str[len] > 0x7f)
+ CHAR ch = str[len];
+
+ if (ch < 0x20 || ch > 0x7f)
{
- rv = FALSE;
- break;
+ return setError("Validator_ShouldBeASCII", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
}
}
- return rv;
+
+ return resetError();
}
- bool validateASCIINoLeadingSpace(const LLWString &str)
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIImpl;
+Validator validateASCII(validatorASCIIImpl);
+
+class ValidatorASCIINoLeadingSpace : public ValidatorASCII
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
- if (LLStringOps::isSpace(str[0]))
+ if (LLStringOps::isSpace(str.front()))
{
- return FALSE;
+ return false;
}
- return validateASCII(str);
+
+ return ValidatorASCII::validate<CHAR>(str);
}
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIINoLeadingSpaceImpl;
+Validator validateASCIINoLeadingSpace(validatorASCIINoLeadingSpaceImpl);
+
+class ValidatorASCIIWithNewLine : public ValidatorImpl
+{
// Used for multiline text stored on the server.
// Example is landmark description in Places SP.
- bool validateASCIIWithNewLine(const LLWString &str)
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
{
- bool rv = TRUE;
S32 len = str.length();
- while(len--)
+ while (len--)
{
- if ((str[len] < 0x20 && str[len] != 0xA) || str[len] > 0x7f)
+ CHAR ch = str[len];
+
+ if ((ch < 0x20 && ch != 0xA) || ch > 0x7f)
{
- rv = FALSE;
- break;
+ return setError("Validator_ShouldBeNewLineOrASCII", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
}
}
- return rv;
+
+ return resetError();
}
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIWithNewLineImpl;
+Validator validateASCIIWithNewLine(validatorASCIIWithNewLineImpl);
+
+void Validators::declareValues()
+{
+ declare("ascii", validateASCII);
+ declare("float", validateFloat);
+ declare("int", validateInt);
+ declare("positive_s32", validatePositiveS32);
+ declare("non_negative_s32", validateNonNegativeS32);
+ declare("alpha_num", validateAlphaNum);
+ declare("alpha_num_space", validateAlphaNumSpace);
+ declare("ascii_printable_no_pipe", validateASCIIPrintableNoPipe);
+ declare("ascii_printable_no_space", validateASCIIPrintableNoSpace);
+ declare("ascii_with_newline", validateASCIIWithNewLine);
}
+
+} // namespace LLTextValidate
diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h
index 0dfec1b612..2c2941de7f 100644
--- a/indra/llui/lltextvalidate.h
+++ b/indra/llui/lltextvalidate.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file lltextbase.h
* @author Martin Reddy
* @brief The base class of text box/editor, providing Url handling support
@@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library 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
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,27 +34,68 @@
namespace LLTextValidate
{
- typedef boost::function<BOOL (const LLWString &wstr)> validate_func_t;
+ class ValidatorImpl
+ {
+ public:
+ ValidatorImpl() {}
+ virtual ~ValidatorImpl() {}
+
+ virtual bool validate(const std::string& str) = 0;
+ virtual bool validate(const LLWString& str) = 0;
+
+ bool setError(std::string name, LLSD values = LLSD()) { return mLastErrorName = name, mLastErrorValues = values, false; }
+ bool resetError() { return mLastErrorName.clear(), mLastErrorValues.clear(), true; }
+ const std::string& getLastErrorName() const { return mLastErrorName; }
+ const LLSD& getLastErrorValues() const { return mLastErrorValues; }
+
+ void setLastErrorShowTime();
+ U32 getLastErrorShowTime() const { return mLastErrorShowTime; }
+
+ protected:
+ std::string mLastErrorName;
+ LLSD mLastErrorValues;
+ U32 mLastErrorShowTime { 0 };
+ };
- struct ValidateTextNamedFuncs
- : public LLInitParam::TypeValuesHelper<validate_func_t, ValidateTextNamedFuncs>
+ class Validator
{
- static void declareValues();
+ public:
+ Validator() : mImpl(nullptr) {}
+ Validator(ValidatorImpl& impl) : mImpl(&impl) {}
+ Validator(const Validator& validator) : mImpl(validator.mImpl) {}
+ Validator(const Validator* validator) : mImpl(validator->mImpl) {}
+
+ bool validate(const std::string& str) const { return !mImpl || mImpl->validate(str); }
+ bool validate(const LLWString& str) const { return !mImpl || mImpl->validate(str); }
+
+ operator bool() const { return mImpl; }
+
+ static const U32 SHOW_LAST_ERROR_TIMEOUT_SEC = 30;
+ void showLastErrorUsingTimeout(U32 timeout = SHOW_LAST_ERROR_TIMEOUT_SEC);
+
+ private:
+ ValidatorImpl* mImpl;
};
- bool validateFloat(const LLWString &str );
- bool validateInt(const LLWString &str );
- bool validatePositiveS32(const LLWString &str);
- bool validateNonNegativeS32(const LLWString &str);
- bool validateNonNegativeS32NoSpace(const LLWString &str);
- bool validateAlphaNum(const LLWString &str );
- bool validateAlphaNumSpace(const LLWString &str );
- bool validateASCIIPrintableNoPipe(const LLWString &str);
- bool validateASCIIPrintableNoSpace(const LLWString &str);
- bool validateASCII(const LLWString &str);
- bool validateASCIINoLeadingSpace(const LLWString &str);
- bool validateASCIIWithNewLine(const LLWString &str);
-}
+ // Available validators
+ extern Validator validateFloat;
+ extern Validator validateInt;
+ extern Validator validatePositiveS32;
+ extern Validator validateNonNegativeS32;
+ extern Validator validateNonNegativeS32NoSpace;
+ extern Validator validateAlphaNum;
+ extern Validator validateAlphaNumSpace;
+ extern Validator validateASCIIPrintableNoPipe;
+ extern Validator validateASCIIPrintableNoSpace;
+ extern Validator validateASCII;
+ extern Validator validateASCIINoLeadingSpace;
+ extern Validator validateASCIIWithNewLine;
+ // Add available validators to the internal map
+ struct Validators : public LLInitParam::TypeValuesHelper<Validator, Validators>
+ {
+ static void declareValues();
+ };
+};
#endif
diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index ca9c05bb6a..3d404293c6 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -49,6 +49,36 @@ const U32 HOURS_MAX = 12;
const U32 MINUTES_PER_HOUR = 60;
const U32 MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR;
+class LLTimeValidatorImpl : public LLTextValidate::ValidatorImpl
+{
+public:
+ // virtual
+ bool validate(const std::string& str) override
+ {
+ std::string hours = LLTimeCtrl::getHoursString(str);
+ if (!LLTimeCtrl::isHoursStringValid(hours))
+ return setError("ValidatorInvalidHours", LLSD().with("STR", hours));
+
+ std::string minutes = LLTimeCtrl::getMinutesString(str);
+ if (!LLTimeCtrl::isMinutesStringValid(minutes))
+ return setError("ValidatorInvalidMinutes", LLSD().with("STR", minutes));
+
+ std::string ampm = LLTimeCtrl::getAMPMString(str);
+ if (!LLTimeCtrl::isPMAMStringValid(ampm))
+ return setError("ValidatorInvalidAMPM", LLSD().with("STR", ampm));
+
+ return resetError();
+ }
+
+ // virtual
+ bool validate(const LLWString& wstr) override
+ {
+ std::string str = wstring_to_utf8str(wstr);
+
+ return validate(str);
+ }
+} validateTimeImpl;
+LLTextValidate::Validator validateTime(validateTimeImpl);
LLTimeCtrl::Params::Params()
: label_width("label_width"),
@@ -111,7 +141,7 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1));
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace);
- mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1));
+ mEditor->setPrevalidate(validateTime);
mEditor->setText(LLStringExplicit("12:00 AM"));
addChild(mEditor);
@@ -247,15 +277,6 @@ void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor)
mTime = h24 * MINUTES_PER_HOUR + m;
}
-bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr)
-{
- std::string str = wstring_to_utf8str(wstr);
-
- return isHoursStringValid(getHoursString(str)) &&
- isMinutesStringValid(getMinutesString(str)) &&
- isPMAMStringValid(getAMPMString(str));
-}
-
void LLTimeCtrl::increaseMinutes()
{
mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin);
diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h
index ff46bc76ca..a2c016ab6f 100644
--- a/indra/llui/lltimectrl.h
+++ b/indra/llui/lltimectrl.h
@@ -60,6 +60,18 @@ public:
void setTime24(F32 time); // 0.0 - 23.98(3)
+ static std::string getHoursString(const std::string& str);
+ static std::string getMinutesString(const std::string& str);
+ static std::string getAMPMString(const std::string& str);
+
+ static bool isHoursStringValid(const std::string& str);
+ static bool isMinutesStringValid(const std::string& str);
+ static bool isPMAMStringValid(const std::string& str);
+
+ static U32 parseHours(const std::string& str);
+ static U32 parseMinutes(const std::string& str);
+ static bool parseAMPM(const std::string& str);
+
protected:
LLTimeCtrl(const Params&);
friend class LLUICtrlFactory;
@@ -87,8 +99,6 @@ private:
void onDownBtn();
void onTextEntry(LLLineEditor* line_editor);
- bool isTimeStringValid(const LLWString& wstr);
-
void increaseMinutes();
void increaseHours();
@@ -102,18 +112,6 @@ private:
EEditingPart getEditingPart();
- static std::string getHoursString(const std::string& str);
- static std::string getMinutesString(const std::string& str);
- static std::string getAMPMString(const std::string& str);
-
- static bool isHoursStringValid(const std::string& str);
- static bool isMinutesStringValid(const std::string& str);
- static bool isPMAMStringValid(const std::string& str);
-
- static U32 parseHours(const std::string& str);
- static U32 parseMinutes(const std::string& str);
- static bool parseAMPM(const std::string& str);
-
class LLTextBox* mLabelBox;
class LLLineEditor* mEditor;
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 87b5b8b9af..8a6cb683d2 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -1133,8 +1133,7 @@ BOOL LLToolBarButton::handleHover(S32 x, S32 y, MASK mask)
BOOL handled = FALSE;
S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY);
- static LLCachedControl<S32> drag_threshold(*LLUI::getInstance()->mSettingGroups["config"], "DragAndDropDistanceThreshold", 3);
- if (mouse_distance_squared > drag_threshold * drag_threshold
+ if (mouse_distance_squared > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD
&& hasMouseCapture() &&
mStartDragItemCallback && mHandleDragItemCallback)
{
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index de1a87f17c..471602515d 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -53,7 +53,7 @@ class LLWindow;
class LLView;
class LLHelp;
-
+const S32 DRAG_N_DROP_DISTANCE_THRESHOLD = 3;
// this enum is used by the llview.h (viewer) and the llassetstorage.h (viewer and sim)
enum EDragAndDropType
{
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 5a66009b78..69093393d9 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -80,7 +80,7 @@ LLUICtrl::Params::Params()
mouseenter_callback("mouseenter_callback"),
mouseleave_callback("mouseleave_callback"),
control_name("control_name"),
- font("font", LLFontGL::getFontSansSerif()),
+ font("font", LLFontGL::getFontEmojiMedium()),
font_halign("halign"),
font_valign("valign"),
length("length"), // ignore LLXMLNode cruft
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index afe8493501..d21e8dc1c6 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -41,6 +41,7 @@
const BOOL TAKE_FOCUS_YES = TRUE;
const BOOL TAKE_FOCUS_NO = FALSE;
+const S32 DROP_SHADOW_FLOATER = 5;
class LLUICtrl
: public LLView, public boost::signals2::trackable
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index a41b7ba4a8..877585cbef 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -898,6 +898,34 @@ F32 LLView::getTooltipTimeout()
: tooltip_delay);
}
+// virtual
+const std::string LLView::getToolTip() const
+{
+ if (sDebugUnicode)
+ {
+ std::string text = getText();
+ if (!text.empty())
+ {
+ const std::string& name = getName();
+ std::string tooltip = llformat("Name: \"%s\"", name.c_str());
+
+ if (const LLFontGL* font = getFont())
+ {
+ tooltip += llformat("\nFont: %s (%s)",
+ font->getFontDesc().getName().c_str(),
+ font->getFontDesc().getSize().c_str()
+ );
+ }
+
+ tooltip += "\n\n" + utf8str_showBytesUTF8(text);
+
+ return tooltip;
+ }
+ }
+
+ return mToolTipMsg.getString();
+}
+
BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 321afdab31..fefdd83bd4 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -244,7 +244,9 @@ public:
ECursorType getHoverCursor() { return mHoverCursor; }
static F32 getTooltipTimeout();
- virtual const std::string getToolTip() const { return mToolTipMsg.getString(); }
+ virtual const std::string getToolTip() const;
+ virtual const std::string& getText() const { return LLStringUtil::null; }
+ virtual const LLFontGL* getFont() const { return nullptr; }
void sendChildToFront(LLView* child);
void sendChildToBack(LLView* child);
diff --git a/indra/llui/llviewmodel.cpp b/indra/llui/llviewmodel.cpp
index 3847d073b4..93106b344f 100644
--- a/indra/llui/llviewmodel.cpp
+++ b/indra/llui/llviewmodel.cpp
@@ -81,7 +81,7 @@ void LLTextViewModel::setValue(const LLSD& value)
{
// approximate LLSD storage usage
LLViewModel::setValue(value);
- mDisplay = utf8str_to_wstring(value.asString());
+ mDisplay = utf8str_to_wstring(mStringValue = value.asString());
// mDisplay and mValue agree
mUpdateFromDisplay = false;
@@ -101,23 +101,34 @@ void LLTextViewModel::setDisplay(const LLWString& value)
mUpdateFromDisplay = true;
}
-LLSD LLTextViewModel::getValue() const
+inline void updateFromDisplayIfNeeded(const LLTextViewModel* model)
{
- // Has anyone called setDisplay() since the last setValue()? If so, have
- // to convert mDisplay back to UTF8.
- if (mUpdateFromDisplay)
+ // Has anyone called setDisplay() since the last setValue()?
+ // If so, have to convert mDisplay back to UTF8.
+ if (model->mUpdateFromDisplay)
{
- // The fact that we're lazily updating fields in this object should be
- // transparent to clients, which is why this method is left
- // conventionally const. Nor do we particularly want to make these
- // members mutable. Just cast away constness in this one place.
- LLTextViewModel* nthis = const_cast<LLTextViewModel*>(this);
+ // The fact that we're lazily updating fields
+ // in this object should be transparent to clients,
+ // which is why this method is left conventionally const.
+ // Nor do we particularly want to make these members mutable.
+ // Just cast away constness in this one place.
+ LLTextViewModel* nthis = const_cast<LLTextViewModel*>(model);
nthis->mUpdateFromDisplay = false;
- nthis->mValue = wstring_to_utf8str(mDisplay);
+ nthis->mValue = nthis->mStringValue = wstring_to_utf8str(model->mDisplay);
}
- return LLViewModel::getValue();
}
+LLSD LLTextViewModel::getValue() const
+{
+ updateFromDisplayIfNeeded(this);
+ return mValue;
+}
+
+const std::string& LLTextViewModel::getStringValue() const
+{
+ updateFromDisplayIfNeeded(this);
+ return mStringValue;
+}
////////////////////////////////////////////////////////////////////////////
diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h
index de6749f490..6cf2200a81 100644
--- a/indra/llui/llviewmodel.h
+++ b/indra/llui/llviewmodel.h
@@ -100,6 +100,7 @@ public:
// LLViewModel functions
virtual void setValue(const LLSD& value);
virtual LLSD getValue() const;
+ const std::string& getStringValue() const;
// New functions
/// Get the stored value in string form
@@ -114,12 +115,17 @@ public:
void setDisplay(const LLWString& value);
private:
+ std::string mStringValue;
+
/// To avoid converting every widget's stored value from LLSD to LLWString
/// every frame, cache the converted value
LLWString mDisplay;
+
/// As the user edits individual characters (setDisplay()), defer
/// LLWString-to-UTF8 conversions until s/he's done.
bool mUpdateFromDisplay;
+
+ friend void updateFromDisplayIfNeeded(const LLTextViewModel* model);
};
/**