summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
authorMonroe Linden <monroe@lindenlab.com>2010-12-20 11:21:17 -0800
committerMonroe Linden <monroe@lindenlab.com>2010-12-20 11:21:17 -0800
commitdee57bea2526c84fb753abfd08e22c19f1cad21c (patch)
tree7fbe9a8cbb5957b78a37eb13f58f8b6786ec8cac /indra/llui
parent67ca1c0b62cec74495104c0d78fc3743775bfd4e (diff)
parent25eef545fd7f6513ae4c590126aef1dc06494f56 (diff)
Merge from viewer-development.
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt1
-rw-r--r--indra/llui/llaccordionctrltab.cpp3
-rw-r--r--indra/llui/llbutton.cpp8
-rw-r--r--indra/llui/llbutton.h4
-rw-r--r--indra/llui/llcheckboxctrl.cpp57
-rw-r--r--indra/llui/llcombobox.cpp3
-rw-r--r--indra/llui/llfloater.cpp51
-rw-r--r--indra/llui/llfloater.h8
-rw-r--r--indra/llui/llhandle.h8
-rw-r--r--indra/llui/lliconctrl.cpp5
-rw-r--r--indra/llui/lliconctrl.h5
-rw-r--r--indra/llui/lllayoutstack.cpp17
-rw-r--r--indra/llui/lllayoutstack.h4
-rw-r--r--indra/llui/lllineeditor.cpp5
-rw-r--r--indra/llui/lllineeditor.h2
-rw-r--r--indra/llui/llmenugl.cpp50
-rw-r--r--indra/llui/llmenugl.h7
-rw-r--r--indra/llui/llnotifications.cpp230
-rw-r--r--indra/llui/llnotifications.h19
-rw-r--r--indra/llui/llnotificationtemplate.h15
-rw-r--r--indra/llui/llnotificationvisibilityrule.h104
-rw-r--r--indra/llui/llpanel.cpp4
-rw-r--r--indra/llui/llprogressbar.cpp6
-rw-r--r--indra/llui/llprogressbar.h8
-rw-r--r--indra/llui/llradiogroup.cpp44
-rw-r--r--indra/llui/llradiogroup.h12
-rw-r--r--indra/llui/llscrollcontainer.cpp5
-rw-r--r--indra/llui/llscrolllistctrl.cpp45
-rw-r--r--indra/llui/llscrolllistctrl.h2
-rw-r--r--indra/llui/llsdparam.cpp27
-rw-r--r--indra/llui/llsdparam.h2
-rw-r--r--indra/llui/lltextbase.cpp3
-rw-r--r--indra/llui/lltexteditor.cpp10
-rw-r--r--indra/llui/lltexteditor.h1
-rw-r--r--indra/llui/llui.cpp24
-rw-r--r--indra/llui/llui.h2
-rw-r--r--indra/llui/lluicolortable.cpp6
-rw-r--r--indra/llui/lluictrl.cpp37
-rw-r--r--indra/llui/lluictrl.h18
-rw-r--r--indra/llui/lluistring.h1
-rw-r--r--indra/llui/llurlentry.cpp77
-rw-r--r--indra/llui/llurlentry.h14
-rw-r--r--indra/llui/llurlregistry.cpp1
-rw-r--r--indra/llui/llview.cpp25
-rw-r--r--indra/llui/llview.h17
-rw-r--r--indra/llui/tests/llurlentry_test.cpp158
46 files changed, 934 insertions, 221 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 063e3906c7..33ab2e93b5 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -160,6 +160,7 @@ set(llui_HEADER_FILES
llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
+ llnotificationvisibilityrule.h
llpanel.h
llprogressbar.h
llradiogroup.h
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index 9d49c1a831..9e4849c58b 100644
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -203,7 +203,8 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
S32 width = getRect().getWidth();
S32 height = getRect().getHeight();
- gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get(),true);
+ F32 alpha = getCurrentTransparency();
+ gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get() % alpha,true);
LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
bool collapsible = (parent && parent->getCollapsible());
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 65ef3e5f8f..45ceaff696 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -98,7 +98,8 @@ LLButton::Params::Params()
is_toggle("is_toggle", false),
scale_image("scale_image", true),
hover_glow_amount("hover_glow_amount"),
- commit_on_return("commit_on_return", true)
+ commit_on_return("commit_on_return", true),
+ use_draw_context_alpha("use_draw_context_alpha", true)
{
addSynonym(is_toggle, "toggle");
held_down_delay.seconds = 0.5f;
@@ -158,7 +159,8 @@ LLButton::LLButton(const LLButton::Params& p)
mLastDrawCharsCount(0),
mMouseDownSignal(NULL),
mMouseUpSignal(NULL),
- mHeldDownSignal(NULL)
+ mHeldDownSignal(NULL),
+ mUseDrawContextAlpha(p.use_draw_context_alpha)
{
static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
@@ -539,7 +541,7 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
// virtual
void LLButton::draw()
{
- F32 alpha = getDrawContext().mAlpha;
+ F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
bool flash = FALSE;
static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0);
static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0);
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 2d5fefa78c..16aa49b653 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -124,6 +124,8 @@ public:
Optional<F32> hover_glow_amount;
Optional<TimeIntervalParam> held_down_delay;
+ Optional<bool> use_draw_context_alpha;
+
Params();
};
@@ -338,6 +340,8 @@ private:
S32 mImageOverlayTopPad;
S32 mImageOverlayBottomPad;
+ bool mUseDrawContextAlpha;
+
/*
* Space between image_overlay and label
*/
diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp
index bbd8db2645..4fe444c1a4 100644
--- a/indra/llui/llcheckboxctrl.cpp
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -88,27 +88,19 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
tbparams.font(p.font);
}
mLabel = LLUICtrlFactory::create<LLTextBox> (tbparams);
+ mLabel->reshapeToFitText();
addChild(mLabel);
- S32 text_width = mLabel->getTextBoundingRect().getWidth();
- S32 text_height = llround(mFont->getLineHeight());
- LLRect label_rect;
- label_rect.setOriginAndSize(
- llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing,
- llcheckboxctrl_vpad + 1, // padding to get better alignment
- text_width + llcheckboxctrl_hpad,
- text_height );
- mLabel->setShape(label_rect);
-
+ LLRect label_rect = mLabel->getRect();
// Button
// Note: button cover the label by extending all the way to the right.
- LLRect btn_rect;
+ LLRect btn_rect = p.check_button.rect();
btn_rect.setOriginAndSize(
- llcheckboxctrl_hpad,
- llcheckboxctrl_vpad,
- llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width + llcheckboxctrl_hpad,
- llmax( text_height, llcheckboxctrl_btn_size() ) + llcheckboxctrl_vpad);
+ btn_rect.mLeft,
+ btn_rect.mBottom,
+ llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft),
+ llmax( label_rect.getHeight(), btn_rect.mTop));
std::string active_true_id, active_false_id;
std::string inactive_true_id, inactive_false_id;
@@ -174,31 +166,20 @@ void LLCheckBoxCtrl::clear()
void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
{
- //stretch or shrink bounding rectangle of label when rebuilding UI at new scale
- static LLUICachedControl<S32> llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0);
- static LLUICachedControl<S32> llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0);
- static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0);
- static LLUICachedControl<S32> llcheckboxctrl_btn_size ("UICheckboxctrlBtnSize", 0);
- S32 text_width = mLabel->getTextBoundingRect().getWidth();
- S32 text_height = llround(mFont->getLineHeight());
- LLRect label_rect;
- label_rect.setOriginAndSize(
- llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing,
- llcheckboxctrl_vpad,
- text_width,
- text_height );
- mLabel->setShape(label_rect);
-
- LLRect btn_rect;
+ mLabel->reshapeToFitText();
+
+ LLRect label_rect = mLabel->getRect();
+
+ // Button
+ // Note: button cover the label by extending all the way to the right.
+ LLRect btn_rect = mButton->getRect();
btn_rect.setOriginAndSize(
- llcheckboxctrl_hpad,
- llcheckboxctrl_vpad,
- llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width,
- llmax( text_height, llcheckboxctrl_btn_size() ) );
- mButton->setShape( btn_rect );
-
- LLUICtrl::reshape(width, height, called_from_parent);
+ btn_rect.mLeft,
+ btn_rect.mBottom,
+ llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft),
+ llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight()));
+ mButton->setShape(btn_rect);
}
//virtual
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 70014fe4f5..6b06040b8a 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -769,7 +769,8 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask)
return FALSE;
}
// if selection has changed, pop open list
- else if ((mList->getLastSelectedItem() != last_selected_item) || (key == KEY_DOWN) || (key == KEY_UP))
+ else if (mList->getLastSelectedItem() != last_selected_item ||
+ (key == KEY_DOWN || key == KEY_UP) && !mList->isEmpty())
{
showList();
}
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 34d8e9c500..1265733bf5 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1,4 +1,5 @@
/**
+
* @file llfloater.cpp
* @brief LLFloater base class
*
@@ -61,10 +62,6 @@
// use this to control "jumping" behavior when Ctrl-Tabbing
const S32 TABBED_FLOATER_OFFSET = 0;
-// static
-F32 LLFloater::sActiveFloaterTransparency = 0.0f;
-F32 LLFloater::sInactiveFloaterTransparency = 0.0f;
-
std::string LLFloater::sButtonNames[BUTTON_COUNT] =
{
"llfloater_close_btn", //BUTTON_CLOSE
@@ -208,14 +205,14 @@ void LLFloater::initClass()
if (ctrl)
{
ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency));
- sActiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency");
+ updateActiveFloaterTransparency();
}
ctrl = LLUI::sSettingGroups["config"]->getControl("InactiveFloaterTransparency").get();
if (ctrl)
{
ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency));
- sInactiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency");
+ updateInactiveFloaterTransparency();
}
}
@@ -225,7 +222,7 @@ static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFl
LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
: LLPanel(), // intentionally do not pass params here, see initFromParams
- mDragHandle(NULL),
+ mDragHandle(NULL),
mTitle(p.title),
mShortTitle(p.short_title),
mSingleInstance(p.single_instance),
@@ -368,13 +365,13 @@ void LLFloater::layoutDragHandle()
// static
void LLFloater::updateActiveFloaterTransparency()
{
- sActiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency");
+ sActiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency");
}
// static
void LLFloater::updateInactiveFloaterTransparency()
{
- sInactiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency");
+ sInactiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency");
}
void LLFloater::addResizeCtrls()
@@ -1193,6 +1190,7 @@ void LLFloater::setFocus( BOOL b )
last_focus->setFocus(TRUE);
}
}
+ updateTransparency(b ? TT_ACTIVE : TT_INACTIVE);
}
// virtual
@@ -1465,6 +1463,9 @@ void LLFloater::setFrontmost(BOOL take_focus)
// there are more than one floater view
// so we need to query our parent directly
((LLFloaterView*)getParent())->bringToFront(this, take_focus);
+
+ // Make sure to set the appropriate transparency type (STORM-732).
+ updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE);
}
}
@@ -1652,7 +1653,7 @@ void LLFloater::onClickCloseBtn()
// virtual
void LLFloater::draw()
{
- mCurrentTransparency = hasFocus() ? sActiveFloaterTransparency : sInactiveFloaterTransparency;
+ const F32 alpha = getCurrentTransparency();
// draw background
if( isBackgroundVisible() )
@@ -1684,12 +1685,12 @@ void LLFloater::draw()
if (image)
{
// We're using images for this floater's backgrounds
- image->draw(getLocalRect(), overlay_color % mCurrentTransparency);
+ image->draw(getLocalRect(), overlay_color % alpha);
}
else
{
// We're not using images, use old-school flat colors
- gl_rect_2d( left, top, right, bottom, color % mCurrentTransparency );
+ gl_rect_2d( left, top, right, bottom, color % alpha );
// draw highlight on title bar to indicate focus. RDW
if(hasFocus()
@@ -1701,7 +1702,7 @@ void LLFloater::draw()
const LLFontGL* font = LLFontGL::getFontSansSerif();
LLRect r = getRect();
gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
- titlebar_focus_color % mCurrentTransparency, 0, TRUE);
+ titlebar_focus_color % alpha, 0, TRUE);
}
}
}
@@ -1767,10 +1768,32 @@ void LLFloater::drawShadow(LLPanel* panel)
shadow_color.mV[VALPHA] *= 0.5f;
}
gl_drop_shadow(left, top, right, bottom,
- shadow_color % mCurrentTransparency,
+ shadow_color % getCurrentTransparency(),
llround(shadow_offset));
}
+void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type)
+{
+ child_list_t children = *view->getChildList();
+ child_list_t::iterator it = children.begin();
+
+ LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view);
+ if (ctrl)
+ {
+ ctrl->setTransparencyType(transparency_type);
+ }
+
+ for(; it != children.end(); ++it)
+ {
+ updateTransparency(*it, transparency_type);
+ }
+}
+
+void LLFloater::updateTransparency(ETypeTransparency transparency_type)
+{
+ updateTransparency(this, transparency_type);
+}
+
void LLFloater::setCanMinimize(BOOL can_minimize)
{
// if removing minimize/restore button programmatically,
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index fa806bb632..bb96272d02 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -284,6 +284,8 @@ public:
static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }
static LLMultiFloater* getFloaterHost() {return sHostp; }
+
+ void updateTransparency(ETypeTransparency transparency_type);
protected:
@@ -343,6 +345,7 @@ private:
static void updateActiveFloaterTransparency();
static void updateInactiveFloaterTransparency();
+ void updateTransparency(LLView* view, ETypeTransparency transparency_type);
public:
// Called when floater is opened, passes mKey
@@ -411,11 +414,6 @@ private:
bool mDocked;
bool mTornOff;
- F32 mCurrentTransparency;
-
- static F32 sActiveFloaterTransparency;
- static F32 sInactiveFloaterTransparency;
-
static LLMultiFloater* sHostp;
static BOOL sQuitting;
static std::string sButtonNames[BUTTON_COUNT];
diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h
index a43f095d67..8c000eee48 100644
--- a/indra/llui/llhandle.h
+++ b/indra/llui/llhandle.h
@@ -61,13 +61,6 @@ public:
return *this;
}
- template<typename Subclass>
- LLHandle<T>& operator =(const LLHandle<Subclass>& other)
- {
- mTombStone = other.mTombStone;
- return *this;
- }
-
bool isDead() const
{
return mTombStone->getTarget() == NULL;
@@ -99,7 +92,6 @@ public:
{
return lhs.mTombStone > rhs.mTombStone;
}
-protected:
protected:
LLPointer<LLTombStone<T> > mTombStone;
diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp
index 627957061d..47f2cfaf89 100644
--- a/indra/llui/lliconctrl.cpp
+++ b/indra/llui/lliconctrl.cpp
@@ -41,6 +41,7 @@ static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon");
LLIconCtrl::Params::Params()
: image("image_name"),
color("color"),
+ use_draw_context_alpha("use_draw_context_alpha", true),
scale_image("scale_image")
{
tab_stop = false;
@@ -51,6 +52,7 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
: LLUICtrl(p),
mColor(p.color()),
mImagep(p.image),
+ mUseDrawContextAlpha(p.use_draw_context_alpha),
mPriority(0),
mDrawWidth(0),
mDrawHeight(0)
@@ -71,7 +73,8 @@ void LLIconCtrl::draw()
{
if( mImagep.notNull() )
{
- mImagep->draw(getLocalRect(), mColor.get() % getDrawContext().mAlpha );
+ const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
+ mImagep->draw(getLocalRect(), mColor.get() % alpha );
}
LLUICtrl::draw();
diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h
index 79a8b0fb28..e9bdab2d47 100644
--- a/indra/llui/lliconctrl.h
+++ b/indra/llui/lliconctrl.h
@@ -48,6 +48,7 @@ public:
{
Optional<LLUIImage*> image;
Optional<LLUIColor> color;
+ Optional<bool> use_draw_context_alpha;
Ignored scale_image;
Params();
};
@@ -79,6 +80,10 @@ protected:
S32 mDrawWidth ;
S32 mDrawHeight ;
+ // If set to true (default), use the draw context transparency.
+ // If false, will use transparency returned by getCurrentTransparency(). See STORM-698.
+ bool mUseDrawContextAlpha;
+
private:
LLUIColor mColor;
LLPointer<LLUIImage> mImagep;
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 6c0a2a9a10..19ac4c58a8 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -103,6 +103,8 @@ LLLayoutStack::Params::Params()
: orientation("orientation"),
animate("animate", true),
clip("clip", true),
+ open_time_constant("open_time_constant", 0.02f),
+ close_time_constant("close_time_constant", 0.03f),
border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0))
{
name="stack";
@@ -116,7 +118,9 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
mOrientation(p.orientation),
mAnimate(p.animate),
mAnimatedThisFrame(false),
- mClip(p.clip)
+ mClip(p.clip),
+ mOpenTimeConstant(p.open_time_constant),
+ mCloseTimeConstant(p.close_time_constant)
{}
LLLayoutStack::~LLLayoutStack()
@@ -309,9 +313,6 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
S32 total_width = 0;
S32 total_height = 0;
- const F32 ANIM_OPEN_TIME = 0.02f;
- const F32 ANIM_CLOSE_TIME = 0.03f;
-
e_panel_list_t::iterator panel_it;
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
@@ -322,7 +323,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
if (!mAnimatedThisFrame)
{
- (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME));
+ (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant));
if ((*panel_it)->mVisibleAmt > 0.99f)
{
(*panel_it)->mVisibleAmt = 1.f;
@@ -340,7 +341,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
if (!mAnimatedThisFrame)
{
- (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME));
+ (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
if ((*panel_it)->mVisibleAmt < 0.001f)
{
(*panel_it)->mVisibleAmt = 0.f;
@@ -355,11 +356,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
if ((*panel_it)->mCollapsed)
{
- (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME));
+ (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
}
else
{
- (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME));
+ (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
}
if (mOrientation == HORIZONTAL)
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index e43669d893..4ac8ef0ee9 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -58,6 +58,8 @@ public:
Optional<S32> border_size;
Optional<bool> animate,
clip;
+ Optional<F32> open_time_constant,
+ close_time_constant;
Params();
};
@@ -143,6 +145,8 @@ private:
bool mAnimatedThisFrame;
bool mAnimate;
bool mClip;
+ F32 mOpenTimeConstant;
+ F32 mCloseTimeConstant;
}; // end class LLLayoutStack
class LLLayoutPanel : public LLPanel
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 6ad1a0bac4..7e348656a9 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -1530,8 +1530,11 @@ void LLLineEditor::drawBackground()
{
image = mBgImage;
}
+
+ if (!image) return;
- F32 alpha = getDrawContext().mAlpha;
+ F32 alpha = getCurrentTransparency();
+
// optionally draw programmatic border
if (has_focus)
{
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index d9d36b73a3..723423a5b9 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -337,7 +337,7 @@ protected:
std::vector<S32> mPreeditPositions;
LLPreeditor::standouts_t mPreeditStandouts;
- LLHandle<LLView> mContextMenuHandle;
+ LLHandle<LLContextMenu> mContextMenuHandle;
private:
// Instances that by default point to the statics but can be overidden in XML.
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index a6cf86d9b8..6c0d47ef63 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -1462,7 +1462,7 @@ BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
{
BOOL branch_visible = getBranch()->getVisible();
BOOL handled = getBranch()->handleAcceleratorKey(key, mask);
- if (handled && !branch_visible && getVisible())
+ if (handled && !branch_visible && isInVisibleChain())
{
// flash this menu entry because we triggered an invisible menu item
LLMenuHolderGL::setActivatedItem(this);
@@ -2611,6 +2611,7 @@ LLMenuItemGL* LLMenuGL::getHighlightedItem()
LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
{
+ if (mItems.empty()) return NULL;
// highlighting first item on a torn off menu is the
// same as giving focus to it
if (!cur_item && getTornOff())
@@ -2711,6 +2712,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa
LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
{
+ if (mItems.empty()) return NULL;
+
// highlighting first item on a torn off menu is the
// same as giving focus to it
if (!cur_item && getTornOff())
@@ -3045,6 +3048,11 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
const S32 CURSOR_WIDTH = 12;
+ if(menu->getChildList()->empty())
+ {
+ return;
+ }
+
// Save click point for detecting cursor moves before mouse-up.
// Must be in local coords to compare with mouseUp events.
// If the mouse doesn't move, the menu will stay open ala the Mac.
@@ -3125,7 +3133,10 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
mAltKeyTrigger = FALSE;
}
- if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key))
+ if(!result
+ && (key == KEY_F10 && mask == MASK_CONTROL)
+ && !gKeyboard->getKeyRepeated(key)
+ && isInVisibleChain())
{
if (getHighlightedItem())
{
@@ -3508,8 +3519,10 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
else
{
//highlight first enabled one
- pMenu->highlightNextItem(NULL);
- handled = true;
+ if(pMenu->highlightNextItem(NULL))
+ {
+ handled = true;
+ }
}
}
}
@@ -3742,9 +3755,7 @@ public:
LLContextMenuBranch(const Params&);
virtual ~LLContextMenuBranch()
- {
- delete mBranch;
- }
+ {}
// called to rebuild the draw label
virtual void buildDrawLabel( void );
@@ -3752,21 +3763,21 @@ public:
// onCommit() - do the primary funcationality of the menu item.
virtual void onCommit( void );
- LLContextMenu* getBranch() { return mBranch; }
+ LLContextMenu* getBranch() { return mBranch.get(); }
void setHighlight( BOOL highlight );
protected:
void showSubMenu();
- LLContextMenu* mBranch;
+ LLHandle<LLContextMenu> mBranch;
};
LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p)
: LLMenuItemGL(p),
- mBranch( p.branch )
+ mBranch( p.branch()->getHandle() )
{
- mBranch->hide();
- mBranch->setParentMenuItem(this);
+ mBranch.get()->hide();
+ mBranch.get()->setParentMenuItem(this);
}
// called to rebuild the draw label
@@ -3775,12 +3786,12 @@ void LLContextMenuBranch::buildDrawLabel( void )
{
// default enablement is this -- if any of the subitems are
// enabled, this item is enabled. JC
- U32 sub_count = mBranch->getItemCount();
+ U32 sub_count = mBranch.get()->getItemCount();
U32 i;
BOOL any_enabled = FALSE;
for (i = 0; i < sub_count; i++)
{
- LLMenuItemGL* item = mBranch->getItem(i);
+ LLMenuItemGL* item = mBranch.get()->getItem(i);
item->buildDrawLabel();
if (item->getEnabled() && !item->getDrawTextDisabled() )
{
@@ -3802,13 +3813,13 @@ void LLContextMenuBranch::buildDrawLabel( void )
void LLContextMenuBranch::showSubMenu()
{
- LLMenuItemGL* menu_item = mBranch->getParentMenuItem();
+ LLMenuItemGL* menu_item = mBranch.get()->getParentMenuItem();
if (menu_item != NULL && menu_item->getVisible())
{
S32 center_x;
S32 center_y;
localPointToScreen(getRect().getWidth(), getRect().getHeight() , &center_x, &center_y);
- mBranch->show(center_x, center_y);
+ mBranch.get()->show(center_x, center_y);
}
}
@@ -3828,7 +3839,7 @@ void LLContextMenuBranch::setHighlight( BOOL highlight )
}
else
{
- mBranch->hide();
+ mBranch.get()->hide();
}
}
@@ -3859,6 +3870,11 @@ void LLContextMenu::setVisible(BOOL visible)
// Takes cursor position in screen space?
void LLContextMenu::show(S32 x, S32 y)
{
+ if (getChildList()->empty())
+ {
+ // nothing to show, so abort
+ return;
+ }
// Save click point for detecting cursor moves before mouse-up.
// Must be in local coords to compare with mouseUp events.
// If the mouse doesn't move, the menu will stay open ala the Mac.
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 35544402f4..7bde8e83ec 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -678,9 +678,12 @@ public:
BOOL appendContextSubMenu(LLContextMenu *menu);
+ LLHandle<LLContextMenu> getHandle() { mHandle.bind(this); return mHandle; }
+
protected:
- BOOL mHoveredAnyItem;
- LLMenuItemGL* mHoverItem;
+ BOOL mHoveredAnyItem;
+ LLMenuItemGL* mHoverItem;
+ LLRootHandle<LLContextMenu> mHandle;
};
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 15edf270bd..cd0f0e36b0 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -28,6 +28,7 @@
#include "llnotifications.h"
#include "llnotificationtemplate.h"
+#include "llnotificationvisibilityrule.h"
#include "llavatarnamecache.h"
#include "llinstantmessage.h"
@@ -138,12 +139,6 @@ private:
bool filterIgnoredNotifications(LLNotificationPtr notification)
{
- // filter everything if we are to ignore ALL
- if(LLNotifications::instance().getIgnoreAllNotifications())
- {
- return false;
- }
-
LLNotificationFormPtr form = notification->getForm();
// Check to see if the user wants to ignore this alert
return !notification->getForm()->getIgnored();
@@ -178,6 +173,28 @@ bool handleIgnoredNotification(const LLSD& payload)
return false;
}
+bool defaultResponse(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() == "add")
+ {
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (pNotif)
+ {
+ // supply default response
+ pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON));
+ }
+ }
+ return false;
+}
+
+bool visibilityRuleMached(const LLSD& payload)
+{
+ // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification.
+ // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order.
+ return true;
+}
+
+
namespace LLNotificationFilters
{
// a sample filter
@@ -407,10 +424,47 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
{
mUniqueContext.push_back(it->value);
}
+
+ lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl;
+
+ for(LLInitParam::ParamIterator<LLNotificationTemplate::Tag>::const_iterator it = p.tags.begin(),
+ end_it = p.tags.end();
+ it != end_it;
+ ++it)
+ {
+ lldebugs << " tag \"" << std::string(it->value) << "\"" << llendl;
+ mTags.push_back(it->value);
+ }
mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
}
+LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p)
+{
+ if (p.show.isChosen())
+ {
+ mType = p.show.type;
+ mTag = p.show.tag;
+ mName = p.show.name;
+ mVisible = true;
+ }
+ else if (p.hide.isChosen())
+ {
+ mType = p.hide.type;
+ mTag = p.hide.tag;
+ mName = p.hide.name;
+ mVisible = false;
+ }
+ else if (p.respond.isChosen())
+ {
+ mType = p.respond.type;
+ mTag = p.respond.tag;
+ mName = p.respond.name;
+ mVisible = false;
+ mResponse = p.respond.response;
+ }
+}
+
LLNotification::LLNotification(const LLNotification::Params& p) :
mTimestamp(p.time_stamp),
mSubstitutions(p.substitutions),
@@ -680,6 +734,25 @@ bool LLNotification::hasUniquenessConstraints() const
return (mTemplatep ? mTemplatep->mUnique : false);
}
+bool LLNotification::matchesTag(const std::string& tag)
+{
+ bool result = false;
+
+ if(mTemplatep)
+ {
+ std::list<std::string>::iterator it;
+ for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++)
+ {
+ if((*it) == tag)
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
void LLNotification::setIgnored(bool ignore)
{
@@ -1071,12 +1144,12 @@ std::string LLNotificationChannel::summarize()
// LLNotifications implementation
// ---
LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
- LLNotificationComparators::orderByUUID()),
- mIgnoreAllNotifications(false)
+ LLNotificationComparators::orderByUUID()),
+ mIgnoreAllNotifications(false)
{
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
-
- mListener.reset(new LLNotificationsListener(*this));
+
+ mListener.reset(new LLNotificationsListener(*this));
}
@@ -1191,6 +1264,7 @@ LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelN
void LLNotifications::initSingleton()
{
loadTemplates();
+ loadVisibilityRules();
createDefaultChannels();
}
@@ -1198,15 +1272,19 @@ void LLNotifications::createDefaultChannels()
{
// now construct the various channels AFTER loading the notifications,
// because the history channel is going to rewrite the stored notifications file
- LLNotificationChannel::buildChannel("Expiration", "",
+ LLNotificationChannel::buildChannel("Enabled", "",
+ !boost::bind(&LLNotifications::getIgnoreAllNotifications, this));
+ LLNotificationChannel::buildChannel("Expiration", "Enabled",
boost::bind(&LLNotifications::expirationFilter, this, _1));
- LLNotificationChannel::buildChannel("Unexpired", "",
+ LLNotificationChannel::buildChannel("Unexpired", "Enabled",
!boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
LLNotificationChannel::buildChannel("Unique", "Unexpired",
boost::bind(&LLNotifications::uniqueFilter, this, _1));
LLNotificationChannel::buildChannel("Ignore", "Unique",
filterIgnoredNotifications);
- LLNotificationChannel::buildChannel("Visible", "Ignore",
+ LLNotificationChannel::buildChannel("VisibilityRules", "Ignore",
+ boost::bind(&LLNotifications::isVisibleByRules, this, _1));
+ LLNotificationChannel::buildChannel("Visible", "VisibilityRules",
&LLNotificationFilters::includeEverything);
// create special persistent notification channel
@@ -1214,6 +1292,8 @@ void LLNotifications::createDefaultChannels()
new LLPersistentNotificationChannel();
// connect action methods to these channels
+ LLNotifications::instance().getChannel("Enabled")->
+ connectFailedFilter(&defaultResponse);
LLNotifications::instance().getChannel("Expiration")->
connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
// uniqueHandler slot should be added as first slot of the signal due to
@@ -1225,6 +1305,8 @@ void LLNotifications::createDefaultChannels()
// connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
LLNotifications::instance().getChannel("Ignore")->
connectFailedFilter(&handleIgnoredNotification);
+ LLNotifications::instance().getChannel("VisibilityRules")->
+ connectFailedFilter(&visibilityRuleMached);
}
bool LLNotifications::addTemplate(const std::string &name,
@@ -1354,6 +1436,12 @@ bool LLNotifications::loadTemplates()
LLXUIParser parser;
parser.readXUI(root, params, full_filename);
+ if(!params.validateBlock())
+ {
+ llerrs << "Problem reading UI Notifications file: " << full_filename << llendl;
+ return false;
+ }
+
mTemplates.clear();
for(LLInitParam::ParamIterator<LLNotificationTemplate::GlobalString>::const_iterator it = params.strings.begin(), end_it = params.strings.end();
@@ -1403,6 +1491,34 @@ bool LLNotifications::loadTemplates()
return true;
}
+bool LLNotifications::loadVisibilityRules()
+{
+ const std::string xml_filename = "notification_visibility.xml";
+ std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename);
+
+ LLNotificationVisibilityRule::Rules params;
+ LLSimpleXUIParser parser;
+ parser.readXUI(full_filename, params);
+
+ if(!params.validateBlock())
+ {
+ llerrs << "Problem reading UI Notification Visibility Rules file: " << full_filename << llendl;
+ return false;
+ }
+
+ mVisibilityRules.clear();
+
+ for(LLInitParam::ParamIterator<LLNotificationVisibilityRule::Rule>::iterator it = params.rules.begin(),
+ end_it = params.rules.end();
+ it != end_it;
+ ++it)
+ {
+ mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(*it)));
+ }
+
+ return true;
+}
+
// Add a simple notification (from XUI)
void LLNotifications::addFromCallback(const LLSD& name)
{
@@ -1553,6 +1669,94 @@ bool LLNotifications::getIgnoreAllNotifications()
return mIgnoreAllNotifications;
}
+bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
+{
+ if(n->isRespondedTo())
+ {
+ // This avoids infinite recursion in the case where the filter calls respond()
+ return true;
+ }
+
+ VisibilityRuleList::iterator it;
+
+ for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++)
+ {
+ // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule.
+
+ lldebugs
+ << "notification \"" << n->getName() << "\" "
+ << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
+ << "name = \"" << (*it)->mName << "\" "
+ << "tag = \"" << (*it)->mTag << "\" "
+ << "type = \"" << (*it)->mType << "\" "
+ << llendl;
+
+ if(!(*it)->mType.empty())
+ {
+ if((*it)->mType != n->getType())
+ {
+ // Type doesn't match, so skip this rule.
+ continue;
+ }
+ }
+
+ if(!(*it)->mTag.empty())
+ {
+ // check this notification's tag(s) against it->mTag and continue if no match is found.
+ if(!n->matchesTag((*it)->mTag))
+ {
+ // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule.
+ continue;
+ }
+ }
+
+ if(!(*it)->mName.empty())
+ {
+ // check this notification's name against the notification's name and continue if no match is found.
+ if((*it)->mName != n->getName())
+ {
+ // This rule's non-empty name didn't match the notification. Skip this rule.
+ continue;
+ }
+ }
+
+ // If we got here, the rule matches. Don't evaluate subsequent rules.
+ if(!(*it)->mVisible)
+ {
+ // This notification is being hidden.
+
+ if((*it)->mResponse.empty())
+ {
+ // Response property is empty. Cancel this notification.
+ lldebugs << "cancelling notification " << n->getName() << llendl;
+
+ n->cancel();
+ }
+ else
+ {
+ // Response property is not empty. Return the specified response.
+ LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON);
+ // TODO: verify that the response template has an item with the correct name
+ response[(*it)->mResponse] = true;
+
+ lldebugs << "responding to notification " << n->getName() << " with response = " << response << llendl;
+
+ n->respond(response);
+ }
+
+ return false;
+ }
+
+ // If we got here, exit the loop and return true.
+ break;
+ }
+
+ lldebugs << "allowing notification " << n->getName() << llendl;
+
+ return true;
+}
+
+
// ---
// END OF LLNotifications implementation
// =========================================================
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index a607f52b97..34d3537781 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -271,6 +271,11 @@ struct LLNotificationTemplate;
// with smart pointers
typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr;
+
+struct LLNotificationVisibilityRule;
+
+typedef boost::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr;
+
/**
* @class LLNotification
* @brief The object that expresses the details of a notification
@@ -507,7 +512,7 @@ public:
std::string getLabel() const;
std::string getURL() const;
S32 getURLOption() const;
- S32 getURLOpenExternally() const;
+ S32 getURLOpenExternally() const;
const LLNotificationFormPtr getForm();
@@ -579,6 +584,8 @@ public:
bool hasUniquenessConstraints() const;
+ bool matchesTag(const std::string& tag);
+
virtual ~LLNotification() {}
};
@@ -860,6 +867,10 @@ public:
// OK to call more than once because it will reload
bool loadTemplates();
+ // load visibility rules from file;
+ // OK to call more than once because it will reload
+ bool loadVisibilityRules();
+
// Add a simple notification (from XUI)
void addFromCallback(const LLSD& name);
@@ -905,6 +916,8 @@ public:
// test for existence
bool templateExists(const std::string& name);
+ typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList;
+
void forceResponse(const LLNotification::Params& params, S32 option);
void createDefaultChannels();
@@ -920,6 +933,8 @@ public:
void setIgnoreAllNotifications(bool ignore);
bool getIgnoreAllNotifications();
+ bool isVisibleByRules(LLNotificationPtr pNotification);
+
private:
// we're a singleton, so we don't have a public constructor
LLNotifications();
@@ -939,6 +954,8 @@ private:
bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate);
TemplateMap mTemplates;
+ VisibilityRuleList mVisibilityRules;
+
std::string mFileName;
LLNotificationMap mUniqueNotifications;
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index 644d5c4d74..eff572b553 100644
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -158,6 +158,15 @@ struct LLNotificationTemplate
{}
};
+ struct Tag : public LLInitParam::Block<Tag>
+ {
+ Mandatory<std::string> value;
+
+ Tag()
+ : value("value")
+ {}
+ };
+
struct Params : public LLInitParam::Block<Params>
{
Mandatory<std::string> name;
@@ -175,6 +184,7 @@ struct LLNotificationTemplate
Optional<FormRef> form_ref;
Optional<ENotificationPriority,
NotificationPriorityValues> priority;
+ Multiple<Tag> tags;
Params()
@@ -191,7 +201,8 @@ struct LLNotificationTemplate
expire_option("expireOption", -1),
url("url"),
unique("unique"),
- form_ref("")
+ form_ref(""),
+ tags("tag")
{}
};
@@ -278,6 +289,8 @@ struct LLNotificationTemplate
// this is loaded as a name, but looked up to get the UUID upon template load.
// If null, it wasn't specified.
LLUUID mSoundEffect;
+ // List of tags that rules can match against.
+ std::list<std::string> mTags;
};
#endif //LL_LLNOTIFICATION_TEMPLATE_H
diff --git a/indra/llui/llnotificationvisibilityrule.h b/indra/llui/llnotificationvisibilityrule.h
new file mode 100644
index 0000000000..78bdec2a8f
--- /dev/null
+++ b/indra/llui/llnotificationvisibilityrule.h
@@ -0,0 +1,104 @@
+/**
+* @file llnotificationvisibility.h
+* @brief Rules for
+* @author Monroe
+*
+* $LicenseInfo:firstyear=2010&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$
+*/
+
+#ifndef LL_LLNOTIFICATION_VISIBILITY_RULE_H
+#define LL_LLNOTIFICATION_VISIBILITY_RULE_H
+
+#include "llinitparam.h"
+//#include "llnotifications.h"
+
+
+
+// This is the class of object read from the XML file (notification_visibility.xml,
+// from the appropriate local language directory).
+struct LLNotificationVisibilityRule
+{
+ struct Filter : public LLInitParam::Block<Filter>
+ {
+ Optional<std::string> type,
+ tag,
+ name;
+
+ Filter()
+ : type("type"),
+ tag("tag"),
+ name("name")
+ {}
+ };
+
+ struct Respond : public LLInitParam::Block<Respond, Filter>
+ {
+ Mandatory<std::string> response;
+
+ Respond()
+ : response("response")
+ {}
+ };
+
+ struct Rule : public LLInitParam::Choice<Rule>
+ {
+ Alternative<Filter> show;
+ Alternative<Filter> hide;
+ Alternative<Respond> respond;
+
+ Rule()
+ : show("show"),
+ hide("hide"),
+ respond("respond")
+ {}
+ };
+
+ struct Rules : public LLInitParam::Block<Rules>
+ {
+ Multiple<Rule> rules;
+
+ Rules()
+ : rules("")
+ {}
+ };
+
+ LLNotificationVisibilityRule(const Rule& p);
+
+ // If true, this rule makes matching notifications visible. Otherwise, it makes them invisible.
+ bool mVisible;
+
+ // Which response to give when making a notification invisible. An empty string means the notification should be cancelled instead of responded to.
+ std::string mResponse;
+
+ // String to match against the notification's "type". An empty string matches all notifications.
+ std::string mType;
+
+ // String to match against the notification's tag(s). An empty string matches all notifications.
+ std::string mTag;
+
+ // String to match against the notification's name. An empty string matches all notifications.
+ std::string mName;
+
+};
+
+#endif //LL_LLNOTIFICATION_VISIBILITY_RULE_H
+
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index 900e2c789e..b2383106a8 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -194,6 +194,8 @@ void LLPanel::draw()
// draw background
if( mBgVisible )
{
+ alpha = getCurrentTransparency();
+
LLRect local_rect = getLocalRect();
if (mBgOpaque )
{
@@ -434,7 +436,7 @@ void LLPanel::initFromParams(const LLPanel::Params& p)
//and LLView::initFromParams will use them to set visible and enabled
setVisible(p.visible);
setEnabled(p.enabled);
-
+ setFocusRoot(p.focus_root);
setSoundFlags(p.sound_flags);
// control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp
index aaa328754d..ead22686bc 100644
--- a/indra/llui/llprogressbar.cpp
+++ b/indra/llui/llprogressbar.cpp
@@ -50,7 +50,7 @@ LLProgressBar::Params::Params()
LLProgressBar::LLProgressBar(const LLProgressBar::Params& p)
-: LLView(p),
+: LLUICtrl(p),
mImageBar(p.image_bar),
mImageFill(p.image_fill),
mColorBackground(p.color_bg()),
@@ -80,7 +80,7 @@ void LLProgressBar::draw()
mImageFill->draw(progress_rect, bar_color);
}
-void LLProgressBar::setPercent(const F32 percent)
+void LLProgressBar::setValue(const LLSD& value)
{
- mPercentDone = llclamp(percent, 0.f, 100.f);
+ mPercentDone = llclamp((F32)value.asReal(), 0.f, 100.f);
}
diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h
index 13297f7493..3f308e7496 100644
--- a/indra/llui/llprogressbar.h
+++ b/indra/llui/llprogressbar.h
@@ -27,14 +27,14 @@
#ifndef LL_LLPROGRESSBAR_H
#define LL_LLPROGRESSBAR_H
-#include "llview.h"
+#include "lluictrl.h"
#include "llframetimer.h"
class LLProgressBar
- : public LLView
+ : public LLUICtrl
{
public:
- struct Params : public LLInitParam::Block<Params, LLView::Params>
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<LLUIImage*> image_bar,
image_fill;
@@ -47,7 +47,7 @@ public:
LLProgressBar(const Params&);
virtual ~LLProgressBar();
- void setPercent(const F32 percent);
+ void setValue(const LLSD& value);
/*virtual*/ void draw();
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index cc348fdc63..6e9586369f 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -69,7 +69,7 @@ protected:
static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item");
LLRadioGroup::Params::Params()
-: has_border("draw_border"),
+: allow_deselect("allow_deselect"),
items("item")
{
addSynonym(items, "radio_item");
@@ -85,18 +85,8 @@ LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
: LLUICtrl(p),
mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()),
mSelectedIndex(-1),
- mHasBorder(p.has_border)
-{
- if (mHasBorder)
- {
- LLViewBorder::Params params;
- params.name("radio group border");
- params.rect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0));
- params.bevel_style(LLViewBorder::BEVEL_NONE);
- LLViewBorder * vb = LLUICtrlFactory::create<LLViewBorder> (params);
- addChild (vb);
- }
-}
+ mAllowDeselect(p.allow_deselect)
+{}
void LLRadioGroup::initFromParams(const Params& p)
{
@@ -184,7 +174,7 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
{
- if (index < 0 || (S32)mRadioButtons.size() <= index )
+ if ((S32)mRadioButtons.size() <= index )
{
return FALSE;
}
@@ -202,13 +192,16 @@ BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
mSelectedIndex = index;
- LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
- radio_item->setTabStop(true);
- radio_item->setValue( TRUE );
-
- if (hasFocus())
+ if (mSelectedIndex >= 0)
{
- mRadioButtons[mSelectedIndex]->focusFirstItem(FALSE, FALSE);
+ LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
+ radio_item->setTabStop(true);
+ radio_item->setValue( TRUE );
+
+ if (hasFocus())
+ {
+ radio_item->focusFirstItem(FALSE, FALSE);
+ }
}
if (!from_event)
@@ -307,8 +300,15 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
LLRadioCtrl* radio = *iter;
if (radio == clicked_radio)
{
- // llinfos << "clicked button " << index << llendl;
- setSelectedIndex(index);
+ if (index == mSelectedIndex && mAllowDeselect)
+ {
+ // don't select anything
+ setSelectedIndex(-1);
+ }
+ else
+ {
+ setSelectedIndex(index);
+ }
// BUG: Calls click callback even if button didn't actually change
onCommit();
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index 0588900600..8bd5698538 100644
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
@@ -49,7 +49,7 @@ public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<bool> has_border;
+ Optional<bool> allow_deselect;
Multiple<ItemParams, AtLeast<1> > items;
Params();
};
@@ -73,7 +73,6 @@ public:
void setIndexEnabled(S32 index, BOOL enabled);
// return the index value of the selected item
S32 getSelectedIndex() const { return mSelectedIndex; }
-
// set the index value programatically
BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE);
@@ -103,12 +102,13 @@ public:
/*virtual*/ BOOL operateOnAll(EOperation op);
private:
- const LLFontGL* mFont;
- S32 mSelectedIndex;
+ const LLFontGL* mFont;
+ S32 mSelectedIndex;
+
typedef std::vector<class LLRadioCtrl*> button_list_t;
- button_list_t mRadioButtons;
+ button_list_t mRadioButtons;
- BOOL mHasBorder;
+ bool mAllowDeselect; // user can click on an already selected option to deselect it
};
#endif
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 3146418a7d..380c477eb2 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -422,9 +422,10 @@ void LLScrollContainer::draw()
// Draw background
if( mIsOpaque )
{
+ F32 alpha = getCurrentTransparency();
+
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.color4fv( mBackgroundColor.get().mV );
- gl_rect_2d( mInnerRect );
+ gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha);
}
// Draw mScrolledViews and update scroll bars.
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 7df7c13dc0..b7848ec37c 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -322,6 +322,7 @@ LLScrollListCtrl::~LLScrollListCtrl()
delete mSortCallback;
std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+ std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());
}
@@ -1482,8 +1483,9 @@ void LLScrollListCtrl::draw()
// Draw background
if (mBackgroundVisible)
{
+ F32 alpha = getCurrentTransparency();
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() : mBgReadOnlyColor.get() );
+ gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha );
}
if (mColumnsDirty)
@@ -2370,10 +2372,10 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar )
void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending)
{
- std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.find(name);
+ column_map_t::iterator itor = mColumns.find(name);
if (itor != mColumns.end())
{
- sortByColumnIndex((*itor).second.mIndex, ascending);
+ sortByColumnIndex((*itor).second->mIndex, ascending);
}
}
@@ -2419,11 +2421,11 @@ void LLScrollListCtrl::dirtyColumns()
// just in case someone indexes into it immediately
mColumnsIndexed.resize(mColumns.size());
- std::map<std::string, LLScrollListColumn>::iterator column_itor;
+ column_map_t::iterator column_itor;
for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
{
- LLScrollListColumn *column = &column_itor->second;
- mColumnsIndexed[column_itor->second.mIndex] = column;
+ LLScrollListColumn *column = column_itor->second;
+ mColumnsIndexed[column_itor->second->mIndex] = column;
}
}
@@ -2581,8 +2583,8 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params
if (mColumns.find(name) == mColumns.end())
{
// Add column
- mColumns[name] = LLScrollListColumn(column_params, this);
- LLScrollListColumn* new_column = &mColumns[name];
+ mColumns[name] = new LLScrollListColumn(column_params, this);
+ LLScrollListColumn* new_column = mColumns[name];
new_column->mIndex = mColumns.size()-1;
// Add button
@@ -2604,14 +2606,14 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params
S32 top = mItemListRect.mTop;
S32 left = mItemListRect.mLeft;
- for (std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.begin();
+ for (column_map_t::iterator itor = mColumns.begin();
itor != mColumns.end();
++itor)
{
- if (itor->second.mIndex < new_column->mIndex &&
- itor->second.getWidth() > 0)
+ if (itor->second->mIndex < new_column->mIndex &&
+ itor->second->getWidth() > 0)
{
- left += itor->second.getWidth() + mColumnPadding;
+ left += itor->second->getWidth() + mColumnPadding;
}
}
@@ -2667,8 +2669,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata)
if (column->mSortingColumn != column->mName
&& parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())
{
- LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn];
- column_index = info_redir.mIndex;
+ LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn];
+ column_index = info_redir->mIndex;
}
// if this column is the primary sort key, reverse the direction
@@ -2701,16 +2703,17 @@ BOOL LLScrollListCtrl::hasSortOrder() const
void LLScrollListCtrl::clearColumns()
{
- std::map<std::string, LLScrollListColumn>::iterator itor;
+ column_map_t::iterator itor;
for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
{
- LLScrollColumnHeader *header = itor->second.mHeader;
+ LLScrollColumnHeader *header = itor->second->mHeader;
if (header)
{
removeChild(header);
delete header;
}
}
+ std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());
mColumns.clear();
mSortColumns.clear();
mTotalStaticColumnWidth = 0;
@@ -2744,7 +2747,7 @@ LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name)
column_map_t::iterator column_itor = mColumns.find(name);
if (column_itor != mColumns.end())
{
- return &column_itor->second;
+ return column_itor->second;
}
return NULL;
}
@@ -2805,7 +2808,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS
new_column.width.pixel_width = cell_p.width;
}
addColumn(new_column);
- columnp = &mColumns[column];
+ columnp = mColumns[column];
new_item->setNumColumns(mColumns.size());
}
@@ -2842,7 +2845,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS
LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value));
if (cell)
{
- LLScrollListColumn* columnp = &(mColumns.begin()->second);
+ LLScrollListColumn* columnp = mColumns.begin()->second;
new_item->setColumn(0, cell);
if (columnp->mHeader
@@ -2857,10 +2860,10 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS
// add dummy cells for missing columns
for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it)
{
- S32 column_idx = column_it->second.mIndex;
+ S32 column_idx = column_it->second->mIndex;
if (new_item->getColumn(column_idx) == NULL)
{
- LLScrollListColumn* column_ptr = &column_it->second;
+ LLScrollListColumn* column_ptr = column_it->second;
LLScrollListCell::Params cell_p;
cell_p.width = column_ptr->getWidth();
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 8a2f893ba2..09ab89960d 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -491,7 +491,7 @@ private:
mutable bool mSorted;
- typedef std::map<std::string, LLScrollListColumn> column_map_t;
+ typedef std::map<std::string, LLScrollListColumn*> column_map_t;
column_map_t mColumns;
BOOL mDirty;
diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp
index f97f80ab6c..9ad13054cb 100644
--- a/indra/llui/llsdparam.cpp
+++ b/indra/llui/llsdparam.cpp
@@ -45,6 +45,7 @@ LLParamSDParser::LLParamSDParser()
if (sReadFuncs.empty())
{
+ registerParserFuncs<LLInitParam::NoParamValue>(readNoValue, &LLParamSDParser::writeNoValue);
registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>);
registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param);
registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>);
@@ -71,6 +72,18 @@ bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const voi
return true;
}
+bool LLParamSDParser::writeNoValue(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack)
+{
+ LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
+ if (!sdparser.mWriteRootSD) return false;
+
+ LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack);
+ if (!sd_to_write) return false;
+
+ return true;
+}
+
+
void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent)
{
mCurReadSD = NULL;
@@ -87,6 +100,8 @@ void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block)
block.serializeBlock(*this);
}
+const LLSD NO_VALUE_MARKER;
+
void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block)
{
if (sd.isMap())
@@ -110,6 +125,11 @@ void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block
readSDValues(*it, block);
}
}
+ else if (sd.isUndefined())
+ {
+ mCurReadSD = &NO_VALUE_MARKER;
+ block.submitValue(mNameStack, *this);
+ }
else
{
mCurReadSD = &sd;
@@ -206,6 +226,13 @@ LLSD* LLParamSDParser::getSDWriteNode(const parser_t::name_stack_t& name_stack)
return sd_to_write;
}
+bool LLParamSDParser::readNoValue(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+ return self.mCurReadSD == &NO_VALUE_MARKER;
+}
+
+
bool LLParamSDParser::readS32(Parser& parser, void* val_ptr)
{
LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h
index 97e8b58e49..69dab2b411 100644
--- a/indra/llui/llsdparam.h
+++ b/indra/llui/llsdparam.h
@@ -63,7 +63,9 @@ private:
LLSD* getSDWriteNode(const parser_t::name_stack_t& name_stack);
static bool writeU32Param(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack);
+ static bool writeNoValue(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack);
+ static bool readNoValue(Parser& parser, void* val_ptr);
static bool readS32(Parser& parser, void* val_ptr);
static bool readU32(Parser& parser, void* val_ptr);
static bool readF32(Parser& parser, void* val_ptr);
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 3f213ed13e..49537ef78f 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1005,6 +1005,7 @@ void LLTextBase::draw()
if (mBGVisible)
{
+ F32 alpha = getCurrentTransparency();
// clip background rect against extents, if we support scrolling
LLRect bg_rect = mVisibleTextRect;
if (mScroller)
@@ -1016,7 +1017,7 @@ void LLTextBase::draw()
: hasFocus()
? mFocusBgColor.get()
: mWriteableBgColor.get();
- gl_rect_2d(doc_rect, bg_color, TRUE);
+ gl_rect_2d(doc_rect, bg_color % alpha, TRUE);
}
// draw document view
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 94bf716e7d..5a46c7c98e 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -277,6 +277,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
updateRects();
}
+
+ mParseOnTheFly = TRUE;
}
void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
@@ -324,8 +326,10 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param
blockUndo();
deselect();
-
+
+ mParseOnTheFly = FALSE;
LLTextBase::setText(utf8str, input_params);
+ mParseOnTheFly = TRUE;
resetDirty();
}
@@ -1367,6 +1371,7 @@ void LLTextEditor::pastePrimary()
// paste from primary (itsprimary==true) or clipboard (itsprimary==false)
void LLTextEditor::pasteHelper(bool is_primary)
{
+ mParseOnTheFly = FALSE;
bool can_paste_it;
if (is_primary)
{
@@ -1450,6 +1455,7 @@ void LLTextEditor::pasteHelper(bool is_primary)
deselect();
onKeyStroke();
+ mParseOnTheFly = TRUE;
}
@@ -2385,7 +2391,7 @@ void LLTextEditor::loadKeywords(const std::string& filename,
void LLTextEditor::updateSegments()
{
- if (mReflowIndex < S32_MAX && mKeywords.isLoaded())
+ if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly)
{
LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
// HACK: No non-ascii keywords for now
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 58ecefdccb..9e4b95003b 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -315,6 +315,7 @@ private:
BOOL mAllowEmbeddedItems;
bool mShowContextMenu;
+ bool mParseOnTheFly;
LLUUID mSourceID;
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index 19c42bf61a..c300fe55d9 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -950,7 +950,7 @@ void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor
}
// Draw gray and white checkerboard with black border
-void gl_rect_2d_checkerboard(const LLRect& rect)
+void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha)
{
// Initialize the first time this is called.
const S32 PIXELS = 32;
@@ -971,11 +971,11 @@ void gl_rect_2d_checkerboard(const LLRect& rect)
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
// ...white squares
- gGL.color3f( 1.f, 1.f, 1.f );
+ gGL.color4f( 1.f, 1.f, 1.f, alpha );
gl_rect_2d(rect);
// ...gray squares
- gGL.color3f( .7f, .7f, .7f );
+ gGL.color4f( .7f, .7f, .7f, alpha );
gGL.flush();
glPolygonStipple( checkerboard );
@@ -1596,23 +1596,25 @@ void LLUI::initClass(const settings_map_t& settings,
sWindow = NULL; // set later in startup
LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow");
+ LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar();
+
// Callbacks for associating controls with floater visibilty:
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2));
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2));
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2));
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2));
+ reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2));
+ reg.add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2));
+ reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2));
+ reg.add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2));
// Button initialization callback for toggle buttons
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2));
+ reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2));
// Button initialization callback for toggle buttons on dockale floaters
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));
+ reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));
// Display the help topic for the current context
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2));
+ reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2));
// Currently unused, but kept for reference:
- LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2));
+ reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2));
// Used by menus along with Floater.Toggle to display visibility as a checkmark
LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::floaterInstanceVisible, _2));
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index fc545c85d5..62d10df8b2 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -79,7 +79,7 @@ void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LL
void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, BOOL filled = TRUE );
void gl_rect_2d(const LLRect& rect, BOOL filled = TRUE );
void gl_rect_2d(const LLRect& rect, const LLColor4& color, BOOL filled = TRUE );
-void gl_rect_2d_checkerboard(const LLRect& rect);
+void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f);
void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines);
diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp
index 0641f6d175..9455d09cc0 100644
--- a/indra/llui/lluicolortable.cpp
+++ b/indra/llui/lluicolortable.cpp
@@ -215,6 +215,12 @@ bool LLUIColorTable::loadFromSettings()
result |= loadFromFilename(current_filename, mLoadedColors);
}
+ current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml");
+ if(current_filename != default_filename)
+ {
+ result |= loadFromFilename(current_filename, mLoadedColors);
+ }
+
std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
loadFromFilename(user_filename, mUserSetColors);
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index d81f425cce..0a06b5e74f 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -36,6 +36,9 @@
static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl");
+F32 LLUICtrl::sActiveControlTransparency = 1.0f;
+F32 LLUICtrl::sInactiveControlTransparency = 1.0f;
+
// Compiler optimization, generate extern template
template class LLUICtrl* LLView::getChild<class LLUICtrl>(
const std::string& name, BOOL recurse) const;
@@ -110,7 +113,8 @@ LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)
mMouseUpSignal(NULL),
mRightMouseDownSignal(NULL),
mRightMouseUpSignal(NULL),
- mDoubleClickSignal(NULL)
+ mDoubleClickSignal(NULL),
+ mTransparencyType(TT_DEFAULT)
{
mUICtrlHandle.bind(this);
}
@@ -923,6 +927,37 @@ BOOL LLUICtrl::getTentative() const
void LLUICtrl::setColor(const LLColor4& color)
{ }
+F32 LLUICtrl::getCurrentTransparency()
+{
+ F32 alpha = 0;
+
+ switch(mTransparencyType)
+ {
+ case TT_DEFAULT:
+ alpha = getDrawContext().mAlpha;
+ break;
+
+ case TT_ACTIVE:
+ alpha = sActiveControlTransparency;
+ break;
+
+ case TT_INACTIVE:
+ alpha = sInactiveControlTransparency;
+ break;
+
+ case TT_FADING:
+ alpha = sInactiveControlTransparency / 2;
+ break;
+ }
+
+ return alpha;
+}
+
+void LLUICtrl::setTransparencyType(ETypeTransparency type)
+{
+ mTransparencyType = type;
+}
+
boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb )
{
if (!mCommitSignal) mCommitSignal = new commit_signal_t();
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 76dfdf754c..b37e9f6b1b 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -120,6 +120,13 @@ public:
Params();
};
+ enum ETypeTransparency
+ {
+ TT_DEFAULT,
+ TT_ACTIVE, // focused floater
+ TT_INACTIVE, // other floaters
+ TT_FADING, // fading toast
+ };
/*virtual*/ ~LLUICtrl();
void initFromParams(const Params& p);
@@ -202,6 +209,11 @@ public:
virtual void setColor(const LLColor4& color);
+ F32 getCurrentTransparency();
+
+ void setTransparencyType(ETypeTransparency type);
+ ETypeTransparency getTransparencyType() const {return mTransparencyType;}
+
BOOL focusNextItem(BOOL text_entry_only);
BOOL focusPrevItem(BOOL text_entry_only);
BOOL focusFirstItem(BOOL prefer_text_fields = FALSE, BOOL focus_flash = TRUE );
@@ -283,6 +295,10 @@ protected:
boost::signals2::connection mMakeVisibleControlConnection;
LLControlVariable* mMakeInvisibleControlVariable;
boost::signals2::connection mMakeInvisibleControlConnection;
+
+ static F32 sActiveControlTransparency;
+ static F32 sInactiveControlTransparency;
+
private:
BOOL mTabStop;
@@ -290,6 +306,8 @@ private:
BOOL mTentative;
LLRootHandle<LLUICtrl> mUICtrlHandle;
+ ETypeTransparency mTransparencyType;
+
class DefaultTabGroupFirstSorter;
};
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index 4faa0e070e..cb40c85582 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -61,7 +61,6 @@ public:
LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {}
LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);
LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); }
-
~LLUIString() { delete mArgs; }
void assign(const std::string& instring);
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 6cc72bad82..4f7b4be526 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -27,6 +27,7 @@
#include "linden_common.h"
#include "llurlentry.h"
+#include "lluictrl.h"
#include "lluri.h"
#include "llurlmatch.h"
#include "llurlregistry.h"
@@ -167,6 +168,15 @@ void LLUrlEntryBase::callObservers(const std::string &id,
}
}
+/// is this a match for a URL that should not be hyperlinked?
+bool LLUrlEntryBase::isLinkDisabled() const
+{
+ // this allows us to have a global setting to turn off text hyperlink highlighting/action
+ bool globally_disabled = LLUI::sSettingGroups["config"]->getBOOL("DisableTextHyperlinkActions");
+
+ return globally_disabled;
+}
+
static std::string getStringAfterToken(const std::string str, const std::string token)
{
size_t pos = str.find(token);
@@ -456,8 +466,8 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
LLStyle::Params LLUrlEntryAgent::getStyle() const
{
LLStyle::Params style_params = LLUrlEntryBase::getStyle();
- style_params.color = LLUIColorTable::instance().getColor("AgentLinkColor");
- style_params.readonly_color = LLUIColorTable::instance().getColor("AgentLinkColor");
+ style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
return style_params;
}
@@ -796,6 +806,69 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const
}
//
+// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g.
+// secondlife:///app/region/Ahern/128/128/0
+//
+LLUrlEntryRegion::LLUrlEntryRegion()
+{
+ mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife:///app/region/Place/X/Y/Z
+ // - secondlife:///app/region/Place/X/Y
+ // - secondlife:///app/region/Place/X
+ // - secondlife:///app/region/Place
+ //
+
+ LLSD path_array = LLURI(url).pathArray();
+ S32 path_parts = path_array.size();
+
+ if (path_parts < 3) // no region name
+ {
+ llwarns << "Failed to parse url [" << url << "]" << llendl;
+ return url;
+ }
+
+ std::string label = unescapeUrl(path_array[2]); // region name
+
+ if (path_parts > 3) // secondlife:///app/region/Place/X
+ {
+ std::string x = path_array[3];
+ label += " (" + x;
+
+ if (path_parts > 4) // secondlife:///app/region/Place/X/Y
+ {
+ std::string y = path_array[4];
+ label += "," + y;
+
+ if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z
+ {
+ std::string z = path_array[5];
+ label = label + "," + z;
+ }
+ }
+
+ label += ")";
+ }
+
+ return label;
+}
+
+std::string LLUrlEntryRegion::getLocation(const std::string &url) const
+{
+ LLSD path_array = LLURI(url).pathArray();
+ std::string region_name = unescapeUrl(path_array[2]);
+ return region_name;
+}
+
+//
// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
// secondlife:///app/teleport/Ahern/50/50/50/
// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 1a16056041..1791739061 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -94,6 +94,8 @@ public:
virtual LLUUID getID(const std::string &string) const { return LLUUID::null; }
+ bool isLinkDisabled() const;
+
protected:
std::string getIDStringFromUrl(const std::string &url) const;
std::string escapeUrl(const std::string &url) const;
@@ -300,6 +302,18 @@ public:
};
///
+/// LLUrlEntryRegion Describes a Second Life location Url, e.g.,
+/// secondlife:///app/region/Ahern/128/128/0
+///
+class LLUrlEntryRegion : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryRegion();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
/// secondlife:///app/teleport/Ahern/50/50/50/
///
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 478b412d5e..523ee5d78c 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -54,6 +54,7 @@ LLUrlRegistry::LLUrlRegistry()
registerUrl(new LLUrlEntryGroup());
registerUrl(new LLUrlEntryParcel());
registerUrl(new LLUrlEntryTeleport());
+ registerUrl(new LLUrlEntryRegion());
registerUrl(new LLUrlEntryWorldMap());
registerUrl(new LLUrlEntryObjectIM());
registerUrl(new LLUrlEntryPlace());
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 3fa86bf0ca..267640a226 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -102,6 +102,7 @@ LLView::Params::Params()
left_pad("left_pad"),
left_delta("left_delta", S32_MAX),
from_xui("from_xui", false),
+ focus_root("focus_root", false),
needs_translate("translate"),
xmlns("xmlns"),
xmlns_xsi("xmlns:xsi"),
@@ -117,7 +118,7 @@ LLView::LLView(const LLView::Params& p)
mParentView(NULL),
mReshapeFlags(FOLLOWS_NONE),
mFromXUI(p.from_xui),
- mIsFocusRoot(FALSE),
+ mIsFocusRoot(p.focus_root),
mLastVisible(FALSE),
mNextInsertionOrdinal(0),
mHoverCursor(getCursorFromString(p.hover_cursor)),
@@ -163,8 +164,6 @@ LLView::~LLView()
if (mDefaultWidgets)
{
- std::for_each(mDefaultWidgets->begin(), mDefaultWidgets->end(),
- DeletePairedPointer());
delete mDefaultWidgets;
mDefaultWidgets = NULL;
}
@@ -1682,18 +1681,7 @@ BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const
//-----------------------------------------------------------------------------
LLView* LLView::getChildView(const std::string& name, BOOL recurse) const
{
- LLView* child = findChildView(name, recurse);
- if (!child)
- {
- child = getDefaultWidget<LLView>(name);
- if (!child)
- {
- LLView::Params view_params;
- view_params.name = name;
- child = LLUICtrlFactory::create<LLView>(view_params);
- }
- }
- return child;
+ return getChild<LLView>(name, recurse);
}
static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets");
@@ -2804,11 +2792,14 @@ LLView::root_to_view_iterator_t LLView::endRootToView()
// only create maps on demand, as they incur heap allocation/deallocation cost
// when a view is constructed/deconstructed
-LLView::default_widget_map_t& LLView::getDefaultWidgetMap() const
+LLView& LLView::getDefaultWidgetContainer() const
{
if (!mDefaultWidgets)
{
- mDefaultWidgets = new default_widget_map_t();
+ LLView::Params p;
+ p.name = "default widget container";
+ p.visible = false; // ensures default widgets can't steal focus, etc.
+ mDefaultWidgets = new LLView(p);
}
return *mDefaultWidgets;
}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index cd2f215c2d..d2bbd663b8 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -116,7 +116,8 @@ public:
visible,
mouse_opaque,
use_bounding_rect,
- from_xui;
+ from_xui,
+ focus_root;
Optional<S32> tab_group,
default_tab_group;
@@ -461,12 +462,8 @@ public:
template <class T> T* getDefaultWidget(const std::string& name) const
{
- default_widget_map_t::const_iterator found_it = getDefaultWidgetMap().find(name);
- if (found_it == getDefaultWidgetMap().end())
- {
- return NULL;
- }
- return dynamic_cast<T*>(found_it->second);
+ LLView* widgetp = getDefaultWidgetContainer().findChildView(name);
+ return dynamic_cast<T*>(widgetp);
}
//////////////////////////////////////////////
@@ -580,9 +577,9 @@ private:
typedef std::map<std::string, LLView*> default_widget_map_t;
// allocate this map no demand, as it is rarely needed
- mutable default_widget_map_t* mDefaultWidgets;
+ mutable LLView* mDefaultWidgets;
- default_widget_map_t& getDefaultWidgetMap() const;
+ LLView& getDefaultWidgetContainer() const;
public:
// Depth in view hierarchy during rendering
@@ -649,7 +646,7 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co
return NULL;
}
- getDefaultWidgetMap()[name] = result;
+ getDefaultWidgetContainer().addChild(result);
}
}
return result;
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 5c6623da61..8f0a48018f 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -27,6 +27,7 @@
#include "linden_common.h"
#include "../llurlentry.h"
+#include "../lluictrl.h"
#include "llurlentry_stub.cpp"
#include "lltut.h"
#include "../lluicolortable.h"
@@ -34,6 +35,14 @@
#include <boost/regex.hpp>
+typedef std::map<std::string, LLControlGroup*> settings_map_t;
+settings_map_t LLUI::sSettingGroups;
+
+BOOL LLControlGroup::getBOOL(const std::string& name)
+{
+ return false;
+}
+
LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
{
return LLUIColor();
@@ -94,6 +103,45 @@ namespace tut
ensure_equals(testname, url, expected);
}
+ void dummyCallback(const std::string &url, const std::string &label, const std::string& icon)
+ {
+ }
+
+ void testLabel(const std::string &testname, LLUrlEntryBase &entry,
+ const char *text, const std::string &expected)
+ {
+ boost::regex regex = entry.getPattern();
+ std::string label = "";
+ boost::cmatch result;
+ bool found = boost::regex_search(text, result, regex);
+ if (found)
+ {
+ S32 start = static_cast<U32>(result[0].first - text);
+ S32 end = static_cast<U32>(result[0].second - text);
+ std::string url = std::string(text+start, end-start);
+ label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3));
+ }
+ ensure_equals(testname, label, expected);
+ }
+
+ void testLocation(const std::string &testname, LLUrlEntryBase &entry,
+ const char *text, const std::string &expected)
+ {
+ boost::regex regex = entry.getPattern();
+ std::string location = "";
+ boost::cmatch result;
+ bool found = boost::regex_search(text, result, regex);
+ if (found)
+ {
+ S32 start = static_cast<U32>(result[0].first - text);
+ S32 end = static_cast<U32>(result[0].second - text);
+ std::string url = std::string(text+start, end-start);
+ location = entry.getLocation(url);
+ }
+ ensure_equals(testname, location, expected);
+ }
+
+
template<> template<>
void object::test<1>()
{
@@ -688,4 +736,114 @@ namespace tut
"<nolink>My Object</nolink>",
"My Object");
}
+
+ template<> template<>
+ void object::test<13>()
+ {
+ //
+ // test LLUrlEntryRegion - secondlife:///app/region/<location> URLs
+ //
+ LLUrlEntryRegion url;
+
+ // Regex tests.
+ testRegex("no valid region", url,
+ "secondlife:///app/region/",
+ "");
+
+ testRegex("invalid coords", url,
+ "secondlife:///app/region/Korea2/a/b/c",
+ "secondlife:///app/region/Korea2/"); // don't count invalid coords
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "secondlife:///app/region/Ahern/50/50/50/",
+ "secondlife:///app/region/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
+ "secondlife:///app/region/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX secondlife:///app/region/Ahern/50/50/50 XXX",
+ "secondlife:///app/region/Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
+ "secondlife:///app/region/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX secondlife:///app/region/Ahern/50/50/ XXX",
+ "secondlife:///app/region/Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX secondlife:///app/region/Ahern/50/50 XXX",
+ "secondlife:///app/region/Ahern/50/50");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("Region with brackets", url,
+ "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("Region with quote", url,
+ "XXX secondlife:///app/region/A'ksha%20Oasis/41/166/701 XXX",
+ "secondlife:///app/region/A%27ksha%20Oasis/41/166/701");
+
+ // Rendering tests.
+ testLabel("Render /app/region/Ahern/50/50/50/", url,
+ "secondlife:///app/region/Ahern/50/50/50/",
+ "Ahern (50,50,50)");
+
+ testLabel("Render /app/region/Ahern/50/50/50", url,
+ "secondlife:///app/region/Ahern/50/50/50",
+ "Ahern (50,50,50)");
+
+ testLabel("Render /app/region/Ahern/50/50/", url,
+ "secondlife:///app/region/Ahern/50/50/",
+ "Ahern (50,50)");
+
+ testLabel("Render /app/region/Ahern/50/50", url,
+ "secondlife:///app/region/Ahern/50/50",
+ "Ahern (50,50)");
+
+ testLabel("Render /app/region/Ahern/50/", url,
+ "secondlife:///app/region/Ahern/50/",
+ "Ahern (50)");
+
+ testLabel("Render /app/region/Ahern/50", url,
+ "secondlife:///app/region/Ahern/50",
+ "Ahern (50)");
+
+ testLabel("Render /app/region/Ahern/", url,
+ "secondlife:///app/region/Ahern/",
+ "Ahern");
+
+ testLabel("Render /app/region/Ahern/ within context", url,
+ "XXX secondlife:///app/region/Ahern/ XXX",
+ "Ahern");
+
+ testLabel("Render /app/region/Ahern", url,
+ "secondlife:///app/region/Ahern",
+ "Ahern");
+
+ testLabel("Render /app/region/Ahern within context", url,
+ "XXX secondlife:///app/region/Ahern XXX",
+ "Ahern");
+
+ testLabel("Render /app/region/Product%20Engine/", url,
+ "secondlife:///app/region/Product%20Engine/",
+ "Product Engine");
+
+ testLabel("Render /app/region/Product%20Engine", url,
+ "secondlife:///app/region/Product%20Engine",
+ "Product Engine");
+
+ // Location parsing texts.
+ testLocation("Location /app/region/Ahern/50/50/50/", url,
+ "secondlife:///app/region/Ahern/50/50/50/",
+ "Ahern");
+
+ testLocation("Location /app/region/Product%20Engine", url,
+ "secondlife:///app/region/Product%20Engine",
+ "Product Engine");
+ }
}