summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgtags1
-rw-r--r--doc/contributions.txt5
-rw-r--r--indra/llui/llurlentry.cpp63
-rw-r--r--indra/llui/llurlentry.h12
-rw-r--r--indra/llui/llurlregistry.cpp1
-rw-r--r--indra/llui/tests/llurlentry_test.cpp149
-rw-r--r--indra/newview/llinspecttoast.cpp11
-rw-r--r--indra/newview/llpanelavatar.cpp18
-rw-r--r--indra/newview/llremoteparcelrequest.cpp13
-rw-r--r--indra/newview/llsidepaneliteminfo.cpp9
-rw-r--r--indra/newview/llsidepaneliteminfo.h1
-rw-r--r--indra/newview/lltoastgroupnotifypanel.cpp1
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml23
-rw-r--r--indra/newview/skins/default/xui/en/panel_my_profile.xml18
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_advanced.xml2
15 files changed, 302 insertions, 25 deletions
diff --git a/.hgtags b/.hgtags
index f90ff079ac..8594a369df 100644
--- a/.hgtags
+++ b/.hgtags
@@ -43,3 +43,4 @@ dc6483491b4af559060bccaef8e9045a303212dd 2.4.0-beta1
dc6483491b4af559060bccaef8e9045a303212dd 2.4.0-beta1
3bc1f50a72e117f4d4ad8d555f0c785ea8cc201e 2.4.0-beta1
25bd6007e3d2fc15db9326ed4b18a24a5969a46a 2.4.0-beta2
+1ed382c6a08ba3850b6ce9061bc551ddece0ea07 2.4.0-release
diff --git a/doc/contributions.txt b/doc/contributions.txt
index be2b56a74c..e38a33b54d 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -374,6 +374,9 @@ Khyota Wulluf
VWR-9966
Kitty Barnett
VWR-19699
+ STORM-288
+ STORM-799
+ STORM-800
Kunnis Basiat
VWR-82
VWR-102
@@ -776,10 +779,12 @@ WolfPup Lowenhar
STORM-143
STORM-255
STORM-256
+ STORM-288
STORM-535
STORM-544
STORM-654
STORM-674
+ STORM-776
VWR-20741
VWR-20933
Zai Lynch
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index e51f28e2e9..4f7b4be526 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -806,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 43a667c390..1791739061 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -302,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/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 59c0826ad7..8f0a48018f 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -103,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>()
{
@@ -697,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");
+ }
}
diff --git a/indra/newview/llinspecttoast.cpp b/indra/newview/llinspecttoast.cpp
index 58b3f0309f..d7b82667d1 100644
--- a/indra/newview/llinspecttoast.cpp
+++ b/indra/newview/llinspecttoast.cpp
@@ -46,6 +46,7 @@ public:
virtual ~LLInspectToast();
/*virtual*/ void onOpen(const LLSD& notification_id);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
private:
void onToastDestroy(LLToast * toast);
@@ -73,6 +74,7 @@ LLInspectToast::~LLInspectToast()
LLTransientFloaterMgr::getInstance()->removeControlView(this);
}
+// virtual
void LLInspectToast::onOpen(const LLSD& notification_id)
{
LLInspect::onOpen(notification_id);
@@ -103,6 +105,15 @@ void LLInspectToast::onOpen(const LLSD& notification_id)
LLUI::positionViewNearMouse(this);
}
+// virtual
+BOOL LLInspectToast::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ // We don't like the way LLInspect handles tooltips
+ // (black tooltips look weird),
+ // so force using the default implementation (STORM-511).
+ return LLFloater::handleToolTip(x, y, mask);
+}
+
void LLInspectToast::onToastDestroy(LLToast * toast)
{
closeFloater(false);
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index 1249d5d856..a9bcdef47c 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -341,10 +341,11 @@ LLPanelAvatarNotes::~LLPanelAvatarNotes()
if(getAvatarId().notNull())
{
LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
- if(LLVoiceClient::instanceExists())
- {
- LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
- }
+ }
+
+ if(LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
}
}
@@ -758,10 +759,11 @@ LLPanelAvatarProfile::~LLPanelAvatarProfile()
if(getAvatarId().notNull())
{
LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
- if(LLVoiceClient::instanceExists())
- {
- LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
- }
+ }
+
+ if(LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
}
}
diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp
index 0dff087553..e5ef51bdd1 100644
--- a/indra/newview/llremoteparcelrequest.cpp
+++ b/indra/newview/llremoteparcelrequest.cpp
@@ -140,22 +140,25 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v
typedef std::vector<observer_multimap_t::iterator> deadlist_t;
deadlist_t dead_iters;
- observer_multimap_t::iterator oi;
- observer_multimap_t::iterator start = observers.lower_bound(parcel_data.parcel_id);
+ observer_multimap_t::iterator oi = observers.lower_bound(parcel_data.parcel_id);
observer_multimap_t::iterator end = observers.upper_bound(parcel_data.parcel_id);
- for (oi = start; oi != end; ++oi)
+ while (oi != end)
{
- LLRemoteParcelInfoObserver * observer = oi->second.get();
+ // increment the loop iterator now since it may become invalid below
+ observer_multimap_t::iterator cur_oi = oi++;
+
+ LLRemoteParcelInfoObserver * observer = cur_oi->second.get();
if(observer)
{
+ // may invalidate cur_oi if the observer removes itself
observer->processParcelInfo(parcel_data);
}
else
{
// the handle points to an expired observer, so don't keep it
// around anymore
- dead_iters.push_back(oi);
+ dead_iters.push_back(cur_oi);
}
}
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp
index be797ea937..c8c6858b81 100644
--- a/indra/newview/llsidepaneliteminfo.cpp
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -71,12 +71,12 @@ void LLItemPropertiesObserver::changed(U32 mask)
const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs();
std::set<LLUUID>::const_iterator it;
- const LLUUID& object_id = mFloater->getObjectID();
+ const LLUUID& item_id = mFloater->getItemID();
for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
{
// set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288)
- if (*it == object_id)
+ if (*it == item_id)
{
// if there's a change we're interested in.
if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
@@ -196,6 +196,11 @@ const LLUUID& LLSidepanelItemInfo::getObjectID() const
return mObjectID;
}
+const LLUUID& LLSidepanelItemInfo::getItemID() const
+{
+ return mItemID;
+}
+
void LLSidepanelItemInfo::reset()
{
LLSidepanelInventorySubpanel::reset();
diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h
index 6416e2cfe4..25be145f64 100644
--- a/indra/newview/llsidepaneliteminfo.h
+++ b/indra/newview/llsidepaneliteminfo.h
@@ -55,6 +55,7 @@ public:
void setEditMode(BOOL edit);
const LLUUID& getObjectID() const;
+ const LLUUID& getItemID() const;
protected:
/*virtual*/ void refresh();
diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp
index 563c27c4d7..75178a6ef8 100644
--- a/indra/newview/lltoastgroupnotifypanel.cpp
+++ b/indra/newview/lltoastgroupnotifypanel.cpp
@@ -77,6 +77,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification
from << from_name << "/" << groupData.mName;
LLTextBox* pTitleText = getChild<LLTextBox>("title");
pTitleText->setValue(from.str());
+ pTitleText->setToolTip(from.str());
//message subject
const std::string& subject = payload["subject"].asString();
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 6c1e9a3082..df556691f0 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1000,6 +1000,29 @@
parameter="perm_prefs" />
</menu_item_call>
</menu>
+ <menu_item_separator/>
+ <menu_item_call
+ enabled="false"
+ label="Undo"
+ name="Undo"
+ shortcut="control|Z">
+ <on_click
+ function="Edit.Undo"
+ userdata="" />
+ <on_enable
+ function="Edit.EnableUndo" />
+ </menu_item_call>
+ <menu_item_call
+ enabled="false"
+ label="Redo"
+ name="Redo"
+ shortcut="control|Y">
+ <on_click
+ function="Edit.Redo"
+ userdata="" />
+ <on_enable
+ function="Edit.EnableRedo" />
+ </menu_item_call>
</menu>
<menu
create_jump_keys="true"
diff --git a/indra/newview/skins/default/xui/en/panel_my_profile.xml b/indra/newview/skins/default/xui/en/panel_my_profile.xml
index 1b41f602cd..5b8abaca6f 100644
--- a/indra/newview/skins/default/xui/en/panel_my_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_my_profile.xml
@@ -185,7 +185,7 @@
</expandable_text>
</panel>
<text
- follows="left|top"
+ follows="left|top|right"
height="15"
font.style="BOLD"
font="SansSerifMedium"
@@ -200,7 +200,7 @@
use_ellipses="true"
/>
<text
- follows="left|top"
+ follows="left|top|right"
font.style="BOLD"
height="10"
layout="topleft"
@@ -213,7 +213,7 @@
<text_editor
allow_scroll="false"
bg_visible="false"
- follows="left|top"
+ follows="left|top|right"
h_pad="0"
height="15"
layout="topleft"
@@ -226,7 +226,7 @@
width="300"
word_wrap="true" />
<text
- follows="left|top"
+ follows="left|top|right"
font.style="BOLD"
height="15"
layout="topleft"
@@ -250,7 +250,7 @@
<text_editor
allow_scroll="false"
bg_visible="false"
- follows="left|top"
+ follows="left|top|right"
h_pad="0"
height="28"
layout="topleft"
@@ -266,7 +266,7 @@
Linden.
</text_editor>
<text
- follows="left|top"
+ follows="left|top|right"
font.style="BOLD"
height="15"
layout="topleft"
@@ -277,7 +277,7 @@
value="Partner:"
width="300" />
<panel
- follows="left|top"
+ follows="left|top|right"
height="15"
layout="topleft"
left="10"
@@ -285,7 +285,7 @@
top_pad="0"
width="300">
<text
- follows="left|top"
+ follows="left|top|right"
height="10"
initial_value="(retrieving)"
layout="topleft"
@@ -297,7 +297,7 @@
width="300" />
</panel>
<text
- follows="left|top"
+ follows="left|top|right"
font.style="BOLD"
height="13"
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
index d6e4c56113..37aab059a9 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
@@ -82,7 +82,7 @@
control_name="AllowMultipleViewers"
follows="top|left"
height="15"
- label="Allow Multiple Viewer"
+ label="Allow Multiple Viewers"
layout="topleft"
left="30"
name="allow_multiple_viewer_check"