diff --git a/indra/llinventory/llsettingsbase.h b/indra/llinventory/llsettingsbase.h index 7de71588ef..bea6fdec97 100644 --- a/indra/llinventory/llsettingsbase.h +++ b/indra/llinventory/llsettingsbase.h @@ -398,7 +398,7 @@ protected: private: bool mLLSDDirty; - bool mDirty; + bool mDirty; // gates updateSettings bool mReplaced; // super dirty! static LLSD combineSDMaps(const LLSD &first, const LLSD &other); diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index efcbc06e18..95bdcd960c 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -1936,6 +1936,7 @@ LLUUID LLSettingsSky::getCloudNoiseTextureId() const void LLSettingsSky::setCloudNoiseTextureId(const LLUUID &id) { mCloudTextureId = id; + setDirtyFlag(true); setLLSDDirty(); } @@ -1980,6 +1981,7 @@ LLVector2 LLSettingsSky::getCloudScrollRate() const void LLSettingsSky::setCloudScrollRate(const LLVector2 &val) { mScrollRate = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2138,6 +2140,7 @@ LLUUID LLSettingsSky::getMoonTextureId() const void LLSettingsSky::setMoonTextureId(LLUUID id) { mMoonTextureId = id; + setDirtyFlag(true); setLLSDDirty(); } @@ -2222,6 +2225,7 @@ LLUUID LLSettingsSky::getSunTextureId() const void LLSettingsSky::setSunTextureId(LLUUID id) { mSunTextureId = id; + setDirtyFlag(true); setLLSDDirty(); } diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp index eb6650c6c5..b8284334ea 100644 --- a/indra/llmessage/llpacketring.cpp +++ b/indra/llmessage/llpacketring.cpp @@ -209,8 +209,14 @@ S32 LLPacketRing::receiveOrDropBufferedPacket(char *datap, bool drop) if (!drop) { - assert(packet_size > 0); - memcpy(datap, packet->getData(), packet_size); + if (packet_size > 0) + { + memcpy(datap, packet->getData(), packet_size); + } + else + { + assert(false); + } } else { diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index e5f24d3f69..621a084365 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -751,7 +751,14 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num); LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num); - image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight()); + if (image_gl && image_raw) + { + image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight()); + } + else + { + llassert(false); //images were just inserted by nextOpenPos, they shouldn't be missing + } return gi; } @@ -944,7 +951,12 @@ bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U1 { LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num); llassert(!mIsFallback); - llassert(image_raw && (image_raw->getComponents() == 4)); + if (!image_raw) + { + llassert(false); + return false; + } + llassert(image_raw->getComponents() == 4); // NOTE: inspired by LLImageRaw::setSubImage() U32* image_data = (U32*)image_raw->getData(); @@ -972,10 +984,17 @@ bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U1 void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const { LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num); - LLImageDataLock lock(image_raw); llassert(!mIsFallback); - llassert(image_raw && (image_raw->getComponents() == 2)); + if (!image_raw) + { + llassert(false); + return; + } + + LLImageDataLock lock(image_raw); + + llassert(image_raw->getComponents() == 2); U8 *target = image_raw->getData(); llassert(target); diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 09a0d38056..e6102fca3d 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -20,6 +20,7 @@ set(llui_SOURCE_FILES llbadgeowner.cpp llbutton.cpp llchatentry.cpp + llchatmentionhelper.cpp llcheckboxctrl.cpp llclipboard.cpp llcombobox.cpp @@ -134,6 +135,7 @@ set(llui_HEADER_FILES llcallbackmap.h llchatentry.h llchat.h + llchatmentionhelper.h llcheckboxctrl.h llclipboard.h llcombobox.h diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp index 42c4c94c22..9dbbca976b 100644 --- a/indra/llui/llchatentry.cpp +++ b/indra/llui/llchatentry.cpp @@ -52,6 +52,7 @@ LLChatEntry::LLChatEntry(const Params& p) mCurrentHistoryLine = mLineHistory.begin(); mAutoIndent = false; + mShowChatMentionPicker = true; keepSelectionOnReturn(true); } diff --git a/indra/llui/llchatmentionhelper.cpp b/indra/llui/llchatmentionhelper.cpp new file mode 100644 index 0000000000..f7769b2cbe --- /dev/null +++ b/indra/llui/llchatmentionhelper.cpp @@ -0,0 +1,151 @@ +/** +* @file llchatmentionhelper.cpp +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, 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$ +*/ + +#include "linden_common.h" + +#include "llchatmentionhelper.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "lluictrl.h" + +constexpr char CHAT_MENTION_HELPER_FLOATER[] = "chat_mention_picker"; + +bool LLChatMentionHelper::isActive(const LLUICtrl* ctrl) const +{ + return mHostHandle.get() == ctrl; +} + +bool LLChatMentionHelper::isCursorInNameMention(const LLWString& wtext, S32 cursor_pos, S32* mention_start_pos) const +{ + if (cursor_pos <= 0 || cursor_pos > static_cast(wtext.size())) + return false; + + // Find the beginning of the current word + S32 start = cursor_pos - 1; + while (start > 0 && wtext[start - 1] != U32(' ') && wtext[start - 1] != U32('\n')) + { + --start; + } + + if (wtext[start] != U32('@')) + return false; + + if (mention_start_pos) + *mention_start_pos = start; + + S32 word_length = cursor_pos - start; + + if (word_length == 1) + { + return true; + } + + // Get the name after '@' + std::string name = wstring_to_utf8str(wtext.substr(start + 1, word_length - 1)); + LLStringUtil::toLower(name); + for (const auto& av_name : mAvatarNames) + { + if (av_name == name || av_name.find(name) == 0) + { + return true; + } + } + + return false; +} + +void LLChatMentionHelper::showHelper(LLUICtrl* host_ctrl, S32 local_x, S32 local_y, const std::string& av_name, std::function cb) +{ + if (mHelperHandle.isDead()) + { + LLFloater* av_picker_floater = LLFloaterReg::getInstance(CHAT_MENTION_HELPER_FLOATER); + mHelperHandle = av_picker_floater->getHandle(); + mHelperCommitConn = av_picker_floater->setCommitCallback([&](LLUICtrl* ctrl, const LLSD& param) { onCommitName(param.asString()); }); + } + setHostCtrl(host_ctrl); + mNameCommitCb = cb; + + S32 floater_x, floater_y; + if (!host_ctrl->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView)) + { + LL_WARNS() << "Cannot show helper for non-floater controls." << LL_ENDL; + return; + } + + LLFloater* av_picker_floater = mHelperHandle.get(); + LLRect rect = av_picker_floater->getRect(); + rect.setLeftTopAndSize(floater_x, floater_y + rect.getHeight(), rect.getWidth(), rect.getHeight()); + av_picker_floater->setRect(rect); + av_picker_floater->openFloater(LLSD().with("av_name", av_name)); +} + +void LLChatMentionHelper::hideHelper(const LLUICtrl* ctrl) +{ + if ((ctrl && !isActive(ctrl))) + { + return; + } + setHostCtrl(nullptr); +} + +bool LLChatMentionHelper::handleKey(const LLUICtrl* ctrl, KEY key, MASK mask) +{ + if (mHelperHandle.isDead() || !isActive(ctrl)) + { + return false; + } + + return mHelperHandle.get()->handleKey(key, mask, true); +} + +void LLChatMentionHelper::onCommitName(std::string name_url) +{ + if (!mHostHandle.isDead() && mNameCommitCb) + { + mNameCommitCb(name_url); + } +} + +void LLChatMentionHelper::setHostCtrl(LLUICtrl* host_ctrl) +{ + const LLUICtrl* pCurHostCtrl = mHostHandle.get(); + if (pCurHostCtrl != host_ctrl) + { + mHostCtrlFocusLostConn.disconnect(); + mHostHandle.markDead(); + mNameCommitCb = {}; + + if (!mHelperHandle.isDead()) + { + mHelperHandle.get()->closeFloater(); + } + + if (host_ctrl) + { + mHostHandle = host_ctrl->getHandle(); + mHostCtrlFocusLostConn = host_ctrl->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); })); + } + } +} diff --git a/indra/llui/llchatmentionhelper.h b/indra/llui/llchatmentionhelper.h new file mode 100644 index 0000000000..5f95d06f31 --- /dev/null +++ b/indra/llui/llchatmentionhelper.h @@ -0,0 +1,66 @@ +/** +* @file llchatmentionhelper.h +* @brief Header file for LLChatMentionHelper +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, 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$ +*/ + +#pragma once + +#include "llhandle.h" +#include "llsingleton.h" + +#include + +class LLFloater; +class LLUICtrl; + +class LLChatMentionHelper : public LLSingleton +{ + LLSINGLETON(LLChatMentionHelper) {} + ~LLChatMentionHelper() override {} + +public: + + bool isActive(const LLUICtrl* ctrl) const; + bool isCursorInNameMention(const LLWString& wtext, S32 cursor_pos, S32* mention_start_pos = nullptr) const; + void showHelper(LLUICtrl* host_ctrl, S32 local_x, S32 local_y, const std::string& av_name, std::function commit_cb); + void hideHelper(const LLUICtrl* ctrl = nullptr); + + bool handleKey(const LLUICtrl* ctrl, KEY key, MASK mask); + void onCommitName(std::string name_url); + + void updateAvatarList(std::vector av_names) { mAvatarNames = av_names; } + +protected: + void setHostCtrl(LLUICtrl* host_ctrl); + LLUICtrl* getHostCtrl() const { return mHostHandle.get(); } + +private: + LLHandle mHostHandle; + LLHandle mHelperHandle; + boost::signals2::connection mHostCtrlFocusLostConn; + boost::signals2::connection mHelperCommitConn; + std::function mNameCommitCb; + + std::vector mAvatarNames; +}; diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index fa4c827d6b..aae8f1c18a 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -474,6 +474,7 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) , mNoItemsCommentTextbox(NULL) , mIsConsecutiveSelection(false) , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape) + , mFocusOnItemClicked(true) , mMagicalHackyHeightPadding(p.magical_hacky_height_padding) { mBorderThickness = getBorderWidth(); @@ -626,7 +627,10 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) return; } - setFocus(true); + if (mFocusOnItemClicked) + { + setFocus(true); + } bool select_item = !isSelected(item_pair); diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index 22626ca5b5..c650458d44 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -307,6 +307,8 @@ public: virtual S32 notify(const LLSD& info) override; + void setFocusOnItemClicked(bool b) { mFocusOnItemClicked = b; } + virtual ~LLFlatListView(); protected: @@ -431,6 +433,8 @@ private: bool mKeepSelectionVisibleOnReshape; + bool mFocusOnItemClicked; + /** All pairs of the list */ pairs_list_t mItemPairs; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index acb0fbfeab..0050531d2f 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2607,9 +2607,24 @@ void LLLineEditor::resetPreedit() if (hasPreeditString()) { const S32 preedit_pos = mPreeditPositions.front(); - mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos); - mText.insert(preedit_pos, mPreeditOverwrittenWString); - setCursor(preedit_pos); + const S32 end = mPreeditPositions.back(); + const S32 len = end - preedit_pos; + const S32 size = mText.length(); + if (preedit_pos < size + && end <= size + && preedit_pos >= 0 + && len > 0) + { + mText.erase(preedit_pos, len); + mText.insert(preedit_pos, mPreeditOverwrittenWString); + setCursor(preedit_pos); + } + else + { + LL_WARNS() << "Index out of bounds. Start: " << preedit_pos + << ", end:" << end + << ", full string length: " << size << LL_ENDL; + } mPreeditWString.clear(); mPreeditOverwrittenWString.clear(); diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 83f61917ad..f022c214d5 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -38,11 +38,13 @@ LLStyle::Params::Params() color("color", LLColor4::black), readonly_color("readonly_color", LLColor4::black), selected_color("selected_color", LLColor4::black), + highlight_bg_color("highlight_bg_color", LLColor4::green), alpha("alpha", 1.f), font("font", LLStyle::getDefaultFont()), image("image"), link_href("href"), is_link("is_link"), + draw_highlight_bg("draw_highlight_bg", false), // Don't highlight URLs on hover if font style contains underline use_default_link_style("use_default_link_style", true), can_underline_on_hover("can_underline_on_hover", true) @@ -55,12 +57,14 @@ LLStyle::LLStyle(const LLStyle::Params& p) mColor(p.color), mReadOnlyColor(p.readonly_color), mSelectedColor(p.selected_color), + mHighlightBgColor(p.highlight_bg_color), mFont(p.font()), mLink(p.link_href), mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()), mDropShadow(p.drop_shadow), mImagep(p.image()), - mAlpha(p.alpha) + mAlpha(p.alpha), + mDrawHighlightBg(p.draw_highlight_bg) {} void LLStyle::setFont(const LLFontGL* font) diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index ff3a9dc63b..6c9dda355b 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -43,12 +43,14 @@ public: Optional drop_shadow; Optional color, readonly_color, - selected_color; + selected_color, + highlight_bg_color; Optional alpha; Optional font; Optional image; Optional link_href; Optional is_link; + Optional draw_highlight_bg; // Don't highlight URLs on hover if font style contains underline Optional use_default_link_style; Optional can_underline_on_hover; @@ -56,6 +58,14 @@ public: Params(); }; LLStyle(const Params& p = Params()); + + enum EUnderlineLink + { + UNDERLINE_ALWAYS = 0, + UNDERLINE_ON_HOVER, + UNDERLINE_NEVER + }; + public: const LLUIColor& getColor() const { return mColor; } void setColor(const LLUIColor &color) { mColor = color; } @@ -88,6 +98,9 @@ public: bool isImage() const { return mImagep.notNull(); } + bool getDrawHighlightBg() const { return mDrawHighlightBg; } + const LLUIColor& getHighlightBgColor() const { return mHighlightBgColor; } + bool operator==(const LLStyle &rhs) const { return @@ -95,11 +108,13 @@ public: && mColor == rhs.mColor && mReadOnlyColor == rhs.mReadOnlyColor && mSelectedColor == rhs.mSelectedColor + && mHighlightBgColor == rhs.mHighlightBgColor && mFont == rhs.mFont && mLink == rhs.mLink && mImagep == rhs.mImagep && mDropShadow == rhs.mDropShadow - && mAlpha == rhs.mAlpha; + && mAlpha == rhs.mAlpha + && mDrawHighlightBg == rhs.mDrawHighlightBg; } bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); } @@ -116,11 +131,13 @@ private: LLUIColor mColor; LLUIColor mReadOnlyColor; LLUIColor mSelectedColor; + LLUIColor mHighlightBgColor; const LLFontGL* mFont; LLPointer mImagep; F32 mAlpha; bool mVisible; bool mIsLink; + bool mDrawHighlightBg; }; typedef LLPointer LLStyleSP; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 68b619f4b4..7f8db4f836 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -599,6 +599,62 @@ std::vector LLTextBase::getSelectionRects() return selection_rects; } +std::vector> LLTextBase::getHighlightedBgRects() +{ + std::vector> highlight_rects; + + LLRect content_display_rect = getVisibleDocumentRect(); + + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator line_iter = + std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); + line_list_t::const_iterator end_iter = + std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); + + for (; line_iter != end_iter; ++line_iter) + { + segment_set_t::iterator segment_iter; + S32 segment_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); + + // Use F32 otherwise a string of multiple segments + // will accumulate a large error + F32 left_precise = (F32)line_iter->mRect.mLeft; + F32 right_precise = (F32)line_iter->mRect.mLeft; + + for (; segment_iter != mSegments.end(); ++segment_iter) + { + LLTextSegmentPtr segmentp = *segment_iter; + + S32 segment_line_start = segmentp->getStart() + segment_offset; + S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); + + if (segment_line_start > segment_line_end) + break; + + F32 segment_width = 0; + S32 segment_height = 0; + + S32 num_chars = segment_line_end - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + right_precise += segment_width; + + if (segmentp->getStyle()->getDrawHighlightBg()) + { + LLRect selection_rect; + selection_rect.mLeft = (S32)left_precise; + selection_rect.mRight = (S32)right_precise; + selection_rect.mBottom = line_iter->mRect.mBottom; + selection_rect.mTop = line_iter->mRect.mTop; + + highlight_rects.push_back(std::pair(selection_rect, segmentp->getStyle()->getHighlightBgColor())); + } + left_precise += segment_width; + } + } + return highlight_rects; +} + // Draws the black box behind the selected text void LLTextBase::drawSelectionBackground() { @@ -668,6 +724,71 @@ void LLTextBase::drawSelectionBackground() } } +void LLTextBase::drawHighlightedBackground() +{ + if (!mLineInfoList.empty()) + { + std::vector> highlight_rects = getHighlightedBgRects(); + + if (highlight_rects.empty()) + return; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLRect content_display_rect = getVisibleDocumentRect(); + + for (std::vector>::iterator rect_it = highlight_rects.begin(); + rect_it != highlight_rects.end(); ++rect_it) + { + LLRect selection_rect = rect_it->first; + const LLColor4& color = rect_it->second; + if (mScroller) + { + // If scroller is On content_display_rect has correct rect and safe to use as is + // Note: we might need to account for border + selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); + } + else + { + // If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position + // and we have to acount for offset depending on position + S32 v_delta = 0; + S32 h_delta = 0; + switch (mVAlign) + { + case LLFontGL::TOP: + v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad; + break; + case LLFontGL::VCENTER: + v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2; + break; + case LLFontGL::BOTTOM: + v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom; + break; + default: + break; + } + switch (mHAlign) + { + case LLFontGL::LEFT: + h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad; + break; + case LLFontGL::HCENTER: + h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2; + break; + case LLFontGL::RIGHT: + h_delta = mVisibleTextRect.mRight - content_display_rect.mRight; + break; + default: + break; + } + selection_rect.translate(h_delta, v_delta); + } + gl_rect_2d(selection_rect, color); + } + } +} + void LLTextBase::drawCursor() { F32 alpha = getDrawContext().mAlpha; @@ -1559,6 +1680,7 @@ void LLTextBase::draw() drawChild(mDocumentView); } + drawHighlightedBackground(); // [SL:KB] - Patch: Control-TextHighlight | Checked: 2013-12-30 (Catznip-3.6) if (mHighlightsDirty) refreshHighlights(); @@ -2438,20 +2560,20 @@ static LLUIImagePtr image_from_icon_name(const std::string& icon_name) } -void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) +void LLTextBase::appendTextImpl(const std::string& new_text, const LLStyle::Params& input_params, bool force_slurl) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; LLStyle::Params style_params(getStyleParams()); style_params.overwriteFrom(input_params); S32 part = (S32)LLTextParser::WHOLE; - if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). + if ((mParseHTML || force_slurl) && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). { S32 start=0,end=0; LLUrlMatch match; std::string text = new_text; while (LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3), isContentTrusted() || mAlwaysShowIcons)) + boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3), isContentTrusted() || mAlwaysShowIcons, force_slurl)) { start = match.getStart(); end = match.getEnd()+1; @@ -2500,9 +2622,9 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para // output the styled Url // FIRE-11437 - Don't supress font style for chat history name links - //appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); + //appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.getUnderline()); appendAndHighlightTextImpl(match.getLabel(), part, link_params, - input_params.can_underline_on_hover ? match.underlineOnHoverOnly() : false); + input_params.can_underline_on_hover ? match.getUnderline() : LLStyle::UNDERLINE_NEVER); // bool tooltip_required = !match.getTooltip().empty(); @@ -2519,11 +2641,11 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para // Custom URI query part color //link_params.color = LLColor4::grey; //link_params.readonly_color = LLColor4::grey; - //appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); + //appendAndHighlightTextImpl(label, part, link_params, match.getUnderline()); static LLUIColor query_part_color = LLUIColorTable::getInstance()->getColor("UriQueryPartColor", LLColor4::grey); link_params.color = query_part_color; link_params.readonly_color = query_part_color; - appendAndHighlightTextImpl(label, part, link_params, input_params.can_underline_on_hover ? match.underlineOnHoverOnly() : false); + appendAndHighlightTextImpl(label, part, link_params, input_params.can_underline_on_hover ? match.getUnderline() : LLStyle::UNDERLINE_NEVER); // // set the tooltip for the query part of url @@ -2715,7 +2837,7 @@ void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const s insertStringNoUndo(getLength(), widget_wide_text, &segments); } -void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only) +void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, e_underline underline_link) { // Save old state S32 selection_start = mSelectionStart; @@ -2745,7 +2867,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig S32 cur_length = getLength(); LLStyleConstSP sp(new LLStyle(highlight_params)); LLTextSegmentPtr segmentp; - if (underline_on_hover_only || mSkipLinkUnderline) + if ((underline_link == e_underline::UNDERLINE_ON_HOVER) || mSkipLinkUnderline) { // Only reset underline font style //highlight_params.font.style("NORMAL"); @@ -2778,7 +2900,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig S32 segment_start = old_length; S32 segment_end = old_length + static_cast(wide_text.size()); LLStyleConstSP sp(new LLStyle(style_params)); - if (underline_on_hover_only || mSkipLinkUnderline) + if ((underline_link == e_underline::UNDERLINE_ON_HOVER) || mSkipLinkUnderline) { LLStyle::Params normal_style_params(style_params); normal_style_params.font.style("NORMAL"); @@ -2812,7 +2934,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig } } -void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only) +void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, e_underline underline_link) { if (new_text.empty()) { @@ -2827,7 +2949,7 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlig if (pos != start) { std::string str = std::string(new_text,start,pos-start); - appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only); + appendAndHighlightTextImpl(str, highlight_part, style_params, underline_link); } appendLineBreakSegment(style_params); start = pos+1; @@ -2835,7 +2957,7 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlig } std::string str = std::string(new_text, start, new_text.length() - start); - appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only); + appendAndHighlightTextImpl(str, highlight_part, style_params, underline_link); } @@ -3689,6 +3811,7 @@ LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 e mLastGeneration(-1) { mFontHeight = mStyle->getFont()->getLineHeight(); + mCanEdit = !mStyle->getDrawHighlightBg(); LLUIImagePtr image = mStyle->getImage(); if (image.notNull()) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index d0f8cafb16..87a5961b7d 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -35,6 +35,7 @@ #include "llstyle.h" #include "llkeywords.h" #include "llpanel.h" +#include "llurlmatch.h" #include #include @@ -139,7 +140,7 @@ public: /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; /*virtual*/ void updateLayout(const class LLTextBase& editor); /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); - /*virtual*/ bool canEdit() const { return true; } + /*virtual*/ bool canEdit() const { return mCanEdit; } /*virtual*/ const LLUIColor& getColor() const { return mStyle->getColor(); } /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; } /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; } @@ -161,6 +162,8 @@ protected: virtual const LLWString& getWText() const; virtual const S32 getLength() const; + void setAllowEdit(bool can_edit) { mCanEdit = can_edit; } + protected: class LLTextBase& mEditor; LLStyleConstSP mStyle; @@ -169,6 +172,8 @@ protected: std::string mTooltip; boost::signals2::connection mImageLoadedConnection; + bool mCanEdit { true }; + // font rendering LLFontVertexBuffer mFontBufferPreSelection; LLFontVertexBuffer mFontBufferSelection; @@ -647,6 +652,7 @@ protected: bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const; }; typedef std::multiset segment_set_t; + typedef LLStyle::EUnderlineLink e_underline; // member functions LLTextBase(const Params &p); @@ -663,12 +669,13 @@ protected: // [/SL:KB] void drawCursor(); void drawText(); + void drawHighlightedBackground(); // modify contents S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted S32 removeStringNoUndo(S32 pos, S32 length); S32 overwriteCharNoUndo(S32 pos, llwchar wc); - void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, bool underline_on_hover_only = false); + void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, e_underline underline_link = e_underline::UNDERLINE_ALWAYS); // manage segments @@ -716,8 +723,9 @@ protected: // avatar names are looked up. void replaceUrl(const std::string &url, const std::string &label, const std::string& icon); - void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params()); - void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false); + void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params(), bool force_slurl = false); + void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, e_underline underline_link = e_underline::UNDERLINE_ALWAYS); + S32 normalizeUri(std::string& uri); protected: // virtual @@ -730,6 +738,7 @@ protected: } std::vector getSelectionRects(); + std::vector> getHighlightedBgRects(); protected: // text segmentation and flow diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 0107eeb78f..3de763de1a 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -60,6 +60,7 @@ #include "llurlregistry.h" #include "lltooltip.h" #include "llmenugl.h" +#include "llchatmentionhelper.h" #include #include "llcombobox.h" @@ -271,6 +272,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mPrevalidator(p.prevalidator()), mShowContextMenu(p.show_context_menu), mShowEmojiHelper(p.show_emoji_helper), + mShowChatMentionPicker(false), mEnableTooltipPaste(p.enable_tooltip_paste), mPassDelete(false), mKeepSelectionOnReturn(false), @@ -780,6 +782,30 @@ void LLTextEditor::handleEmojiCommit(llwchar emoji) } } +void LLTextEditor::handleMentionCommit(std::string name_url) +{ + S32 mention_start_pos; + if (LLChatMentionHelper::instance().isCursorInNameMention(getWText(), mCursorPos, &mention_start_pos)) + { + remove(mention_start_pos, mCursorPos - mention_start_pos, true); + insert(mention_start_pos, utf8str_to_wstring(name_url), false, LLTextSegmentPtr()); + + std::string new_text(wstring_to_utf8str(getConvertedText())); + clear(); + appendTextImpl(new_text, LLStyle::Params(), true); + + segment_set_t::const_iterator it = getSegIterContaining(mention_start_pos); + if (it != mSegments.end()) + { + setCursorPos((*it)->getEnd() + 1); + } + else + { + setCursorPos(mention_start_pos); + } + } +} + bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { bool handled = false; @@ -1179,6 +1205,7 @@ void LLTextEditor::removeCharOrTab() } tryToShowEmojiHelper(); + tryToShowMentionHelper(); } else { @@ -1204,6 +1231,7 @@ void LLTextEditor::removeChar() setCursorPos(mCursorPos - 1); removeChar(mCursorPos); tryToShowEmojiHelper(); + tryToShowMentionHelper(); } else { @@ -1294,6 +1322,7 @@ void LLTextEditor::addChar(llwchar wc) setCursorPos(mCursorPos + addChar( mCursorPos, wc )); tryToShowEmojiHelper(); + tryToShowMentionHelper(); if (!mReadOnly && mAutoreplaceCallback != NULL) { @@ -1352,6 +1381,31 @@ void LLTextEditor::tryToShowEmojiHelper() } } +void LLTextEditor::tryToShowMentionHelper() +{ + if (mReadOnly || !mShowChatMentionPicker) + return; + + S32 mention_start_pos; + LLWString text(getWText()); + if (LLChatMentionHelper::instance().isCursorInNameMention(text, mCursorPos, &mention_start_pos)) + { + const LLRect cursor_rect(getLocalRectFromDocIndex(mention_start_pos)); + std::string name_part(wstring_to_utf8str(text.substr(mention_start_pos, mCursorPos - mention_start_pos))); + name_part.erase(0, 1); + auto cb = [this](std::string name_url) + { + handleMentionCommit(name_url); + }; + LLChatMentionHelper::instance().showHelper(this, cursor_rect.mLeft, cursor_rect.mTop, name_part, cb); + } + else + { + LLChatMentionHelper::instance().hideHelper(); + } +} + + void LLTextEditor::addLineBreakChar(bool group_together) { if( !getEnabled() ) @@ -2066,7 +2120,7 @@ bool LLTextEditor::handleKeyHere(KEY key, MASK mask ) // not handled and let the parent take care of field movement. if (KEY_TAB == key && mTabsToNextField) { - return false; + return mShowChatMentionPicker && LLChatMentionHelper::instance().handleKey(this, key, mask); } // FIRE-19933: Open context menu on context menu key press @@ -2084,9 +2138,13 @@ bool LLTextEditor::handleKeyHere(KEY key, MASK mask ) } else { - if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) + if (!mReadOnly) { - return true; + if ((mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) || + (mShowChatMentionPicker && LLChatMentionHelper::instance().handleKey(this, key, mask))) + { + return true; + } } if (mEnableTooltipPaste && @@ -3302,3 +3360,21 @@ S32 LLTextEditor::spacesPerTab() { return SPACES_PER_TAB; } + +LLWString LLTextEditor::getConvertedText() const +{ + LLWString text = getWText(); + S32 diff = 0; + for (auto segment : mSegments) + { + if (segment && segment->getStyle() && segment->getStyle()->getDrawHighlightBg()) + { + S32 seg_length = segment->getEnd() - segment->getStart(); + std::string slurl = segment->getStyle()->getLinkHREF(); + + text.replace(segment->getStart() + diff, seg_length, utf8str_to_wstring(slurl)); + diff += (S32)slurl.size() - seg_length; + } + } + return text; +} diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 8fc60edbd9..6f152d43a0 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -96,6 +96,8 @@ public: void insertEmoji(llwchar emoji); void handleEmojiCommit(llwchar emoji); + void handleMentionCommit(std::string name_url); + // mousehandler overrides virtual bool handleMouseDown(S32 x, S32 y, MASK mask); virtual bool handleMouseUp(S32 x, S32 y, MASK mask); @@ -227,6 +229,8 @@ public: void setPassDelete(bool b) { mPassDelete = b; } + LLWString getConvertedText() const; + protected: // FIRE-19933: Open context menu on context menu key press //void showContextMenu(S32 x, S32 y); @@ -274,6 +278,7 @@ protected: S32 remove(S32 pos, S32 length, bool group_with_next_op); void tryToShowEmojiHelper(); + void tryToShowMentionHelper(); void focusLostHelper(); void updateAllowingLanguageInput(); bool hasPreeditString() const; @@ -311,6 +316,7 @@ protected: bool mAutoIndent; bool mParseOnTheFly; + bool mShowChatMentionPicker; void updateLinkSegments(); void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; } diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index b8d2232264..47818fcb59 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -29,7 +29,6 @@ #include "llurlentry.h" #include "lluictrl.h" #include "lluri.h" -#include "llurlmatch.h" #include "llurlregistry.h" #include "lluriparser.h" @@ -51,7 +50,7 @@ // Utility functions std::string localize_slapp_label(const std::string& url, const std::string& full_name); - +LLUUID LLUrlEntryBase::sAgentID(LLUUID::null); LLUrlEntryBase::LLUrlEntryBase() { } @@ -71,7 +70,7 @@ std::string LLUrlEntryBase::getIcon(const std::string &url) return mIcon; } -LLStyle::Params LLUrlEntryBase::getStyle() const +LLStyle::Params LLUrlEntryBase::getStyle(const std::string &url) const { LLStyle::Params style_params; style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); @@ -735,10 +734,14 @@ std::string LLUrlEntryAgent::getTooltip(const std::string &string) const return LLTrans::getString("TooltipAgentUrl"); } -bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const +LLStyle::EUnderlineLink LLUrlEntryAgent::getUnderline(const std::string& string) const { std::string url = getUrl(string); - return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect"); + if (LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect")) + { + return LLStyle::EUnderlineLink::UNDERLINE_ON_HOVER; + } + return LLStyle::EUnderlineLink::UNDERLINE_ALWAYS; } std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb) @@ -780,11 +783,12 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } } -LLStyle::Params LLUrlEntryAgent::getStyle() const +LLStyle::Params LLUrlEntryAgent::getStyle(const std::string &url) const { - LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + LLStyle::Params style_params = LLUrlEntryBase::getStyle(url); style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; } @@ -819,6 +823,10 @@ std::string localize_slapp_label(const std::string& url, const std::string& full { return LLTrans::getString("SLappAgentRemoveFriend") + " " + full_name; } + if (LLStringUtil::endsWith(url, "/mention")) + { + return "@" + full_name; + } return full_name; } @@ -830,6 +838,34 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url) return mIcon; } +/// +/// LLUrlEntryAgentMention Describes a chat mention Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/mention +/// +LLUrlEntryAgentMention::LLUrlEntryAgentMention() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/mention", boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_agent.xml"; + mIcon = std::string(); +} + +LLStyle::EUnderlineLink LLUrlEntryAgentMention::getUnderline(const std::string& string) const +{ + return LLStyle::EUnderlineLink::UNDERLINE_NEVER; +} + +LLStyle::Params LLUrlEntryAgentMention::getStyle(const std::string& url) const +{ + LLStyle::Params style_params = LLUrlEntryAgent::getStyle(url); + style_params.font.style = "NORMAL"; + style_params.draw_highlight_bg = true; + + LLUUID agent_id(getIDStringFromUrl(url)); + style_params.highlight_bg_color = LLUIColorTable::instance().getColor((agent_id == sAgentID) ? "ChatSelfMentionHighlight" : "ChatMentionHighlight"); + + return style_params; +} + // // LLUrlEntryAgentName describes a Second Life agent name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) @@ -891,7 +927,7 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } } -LLStyle::Params LLUrlEntryAgentName::getStyle() const +LLStyle::Params LLUrlEntryAgentName::getStyle(const std::string &url) const { // don't override default colors return LLStyle::Params().is_link(false); @@ -1074,9 +1110,9 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa } } -LLStyle::Params LLUrlEntryGroup::getStyle() const +LLStyle::Params LLUrlEntryGroup::getStyle(const std::string &url) const { - LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + LLStyle::Params style_params = LLUrlEntryBase::getStyle(url); style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); return style_params; @@ -1155,7 +1191,6 @@ std::string LLUrlEntryChat::getLabel(const std::string &url, const LLUrlLabelCal } // LLUrlEntryParcel statics. -LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null); LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null); LLHost LLUrlEntryParcel::sRegionHost; bool LLUrlEntryParcel::sDisconnected(false); @@ -1528,17 +1563,17 @@ std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const return LLUrlEntryBase::getTooltip(string); } -bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const +LLStyle::EUnderlineLink LLUrlEntrySLLabel::getUnderline(const std::string& string) const { std::string url = getUrl(string); - LLUrlMatch match; + LLUrlMatch match; if (LLUrlRegistry::instance().findUrl(url, match)) { - return match.underlineOnHoverOnly(); + return match.getUnderline(); } // unrecognized URL? should not happen - return LLUrlEntryBase::underlineOnHoverOnly(string); + return LLUrlEntryBase::getUnderline(string); } // @@ -1602,7 +1637,7 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC return getUrl(url); } -LLStyle::Params LLUrlEntryNoLink::getStyle() const +LLStyle::Params LLUrlEntryNoLink::getStyle(const std::string &url) const { // Don't render as URL (i.e. no context menu or hand cursor). return LLStyle::Params().is_link(false); diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 77146046f5..ff3fae1f2f 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -85,7 +85,7 @@ public: virtual std::string getIcon(const std::string &url); /// Return the style to render the displayed text - virtual LLStyle::Params getStyle() const; + virtual LLStyle::Params getStyle(const std::string &url) const; /// Given a matched Url, return a tooltip string for the hyperlink virtual std::string getTooltip(const std::string &string) const { return mTooltip; } @@ -96,11 +96,12 @@ public: /// Return the name of a SL location described by this Url, if any virtual std::string getLocation(const std::string &url) const { return ""; } - /// Should this link text be underlined only when mouse is hovered over it? - virtual bool underlineOnHoverOnly(const std::string &string) const { return false; } + virtual LLStyle::EUnderlineLink getUnderline(const std::string& string) const { return LLStyle::EUnderlineLink::UNDERLINE_ALWAYS; } virtual bool isTrusted() const { return false; } + virtual bool getSkipProfileIcon(const std::string& string) const { return false; } + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } bool isLinkDisabled() const; @@ -109,6 +110,8 @@ public: virtual bool isSLURLvalid(const std::string &url) const { return true; }; + static void setAgentID(const LLUUID& id) { sAgentID = id; } + protected: std::string getIDStringFromUrl(const std::string &url) const; std::string escapeUrl(const std::string &url) const; @@ -130,6 +133,8 @@ protected: std::string mMenuName; std::string mTooltip; std::multimap mObservers; + + static LLUUID sAgentID; }; /// @@ -239,9 +244,11 @@ public: /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ std::string getIcon(const std::string &url); /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLStyle::Params getStyle(const std::string &url) const; /*virtual*/ LLUUID getID(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; + + LLStyle::EUnderlineLink getUnderline(const std::string& string) const; + protected: /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); private: @@ -251,6 +258,19 @@ private: avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; +/// +/// LLUrlEntryAgentMention Describes a chat mention Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/mention +class LLUrlEntryAgentMention : public LLUrlEntryAgent +{ +public: + LLUrlEntryAgentMention(); + + LLStyle::Params getStyle(const std::string& url) const; + LLStyle::EUnderlineLink getUnderline(const std::string& string) const; + bool getSkipProfileIcon(const std::string& string) const { return true; }; +}; + /// /// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g., /// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) @@ -272,7 +292,7 @@ public: mAvatarNameCacheConnections.clear(); } /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLStyle::Params getStyle(const std::string &url) const; protected: // override this to pull out relevant name fields virtual std::string getName(const LLAvatarName& avatar_name) = 0; @@ -383,7 +403,7 @@ class LLUrlEntryGroup : public LLUrlEntryBase public: LLUrlEntryGroup(); /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLStyle::Params getStyle(const std::string &url) const; /*virtual*/ LLUUID getID(const std::string &string) const; private: void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); @@ -455,17 +475,15 @@ public: // Processes parcel label and triggers notifying observers. static void processParcelInfo(const LLParcelData& parcel_data); - // Next 4 setters are used to update agent and viewer connection information + // Next setters are used to update agent and viewer connection information // upon events like user login, viewer disconnect and user changing region host. // These setters are made public to be accessible from newview and should not be // used in other cases. - static void setAgentID(const LLUUID& id) { sAgentID = id; } static void setSessionID(const LLUUID& id) { sSessionID = id; } static void setRegionHost(const LLHost& host) { sRegionHost = host; } static void setDisconnected(bool disconnected) { sDisconnected = disconnected; } private: - static LLUUID sAgentID; static LLUUID sSessionID; static LLHost sRegionHost; static bool sDisconnected; @@ -557,7 +575,7 @@ public: /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ std::string getUrl(const std::string &string) const; /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; + LLStyle::EUnderlineLink getUnderline(const std::string& string) const; }; /// @@ -581,7 +599,7 @@ public: LLUrlEntryNoLink(); /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLStyle::Params getStyle(const std::string &url) const; }; /// diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index 0801a3da79..103f04a854 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -37,8 +37,9 @@ LLUrlMatch::LLUrlMatch() : mIcon(""), mMenuName(""), mLocation(""), - mUnderlineOnHoverOnly(false), - mTrusted(false) + mUnderline(e_underline::UNDERLINE_ALWAYS), + mTrusted(false), + mSkipProfileIcon(false) { } @@ -48,7 +49,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std const std::string &menu, const std::string &location, // Store matched text const std::string& matched_text, - const LLUUID& id, bool underline_on_hover_only, bool trusted) + const LLUUID& id, e_underline underline, bool trusted, bool skip_icon) { mStart = start; mEnd = end; @@ -62,8 +63,9 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std mMenuName = menu; mLocation = location; mID = id; - mUnderlineOnHoverOnly = underline_on_hover_only; + mUnderline = underline; mTrusted = trusted; + mSkipProfileIcon = skip_icon; // Store matched text mMatchedText = matched_text; } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index e9176d8889..2a32ac1f19 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -82,12 +82,13 @@ public: // Store matched text std::string getMatchedText() const { return mMatchedText; } - /// Should this link text be underlined only when mouse is hovered over it? - bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; } - + typedef LLStyle::EUnderlineLink e_underline; + e_underline getUnderline() const { return mUnderline; } /// Return true if Url is trusted. bool isTrusted() const { return mTrusted; } + bool getSkipProfileIcon() const { return mSkipProfileIcon; } + /// Change the contents of this match object (used by LLUrlRegistry) void setValues(U32 start, U32 end, const std::string &url, const std::string &label, const std::string& query, const std::string &tooltip, const std::string &icon, @@ -96,7 +97,7 @@ public: //const std::string &location, const LLUUID& id, const std::string &location, const std::string& matched_text, const LLUUID& id, // - bool underline_on_hover_only = false, bool trusted = false); + e_underline underline = e_underline::UNDERLINE_ALWAYS, bool trusted = false, bool skip_icon = false); const LLUUID& getID() const { return mID; } private: @@ -113,8 +114,9 @@ private: std::string mMatchedText; LLUUID mID; LLStyle::Params mStyle; - bool mUnderlineOnHoverOnly; + e_underline mUnderline; bool mTrusted; + bool mSkipProfileIcon; }; #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index b4f2385f27..abbb710925 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -41,7 +41,7 @@ LLUrlRegistry::LLUrlRegistry() { // mUrlEntry.reserve(20); // [RLVa:KB] - Checked: 2010-11-01 (RLVa-1.2.2a) | Added: RLVa-1.2.2a - mUrlEntry.reserve(30); + mUrlEntry.reserve(31); // [/RLVa:KB] // Urls are matched in the order that they were registered @@ -74,6 +74,8 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new FSUrlEntryAgentSelf());// FIRE-30611: "You" in transcript is underlined // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since // LLUrlEntryAgent is a less specific (catchall for agent urls) + mUrlEntryAgentMention = new LLUrlEntryAgentMention(); + registerUrl(mUrlEntryAgentMention); registerUrl(new LLUrlEntryAgent()); registerUrl(new LLUrlEntryChat()); registerUrl(new LLUrlEntryGroup()); @@ -219,7 +221,7 @@ static bool stringHasJira(const std::string &text) text.find("WEB") != std::string::npos); } -bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted) +bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted, bool skip_non_mentions) { // avoid costly regexes if there is clearly no URL in the text if (! (stringHasUrl(text) || stringHasJira(text))) @@ -240,6 +242,11 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL continue; } + if (skip_non_mentions && (mUrlEntryAgentMention != *it)) + { + continue; + } + LLUrlEntryBase *url_entry = *it; U32 start = 0, end = 0; @@ -314,14 +321,15 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL match_entry->getQuery(url), match_entry->getTooltip(url), match_entry->getIcon(url), - match_entry->getStyle(), + match_entry->getStyle(url), match_entry->getMenuName(), match_entry->getLocation(url), // Store matched text text.substr(match_start, match_end - match_start + 1), match_entry->getID(url), - match_entry->underlineOnHoverOnly(url), - match_entry->isTrusted()); + match_entry->getUnderline(url), + match_entry->isTrusted(), + match_entry->getSkipProfileIcon(url)); return true; } @@ -364,7 +372,9 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr // Store matched text match.getMatchedText(), match.getID(), - match.underlineOnHoverOnly()); + match.getUnderline(), + false, + match.getSkipProfileIcon()); return true; } return false; diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h index d2648e6312..a4aff1ba53 100644 --- a/indra/llui/llurlregistry.h +++ b/indra/llui/llurlregistry.h @@ -75,7 +75,7 @@ public: /// your callback is invoked if the matched Url's label changes in the future bool findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback, - bool is_content_trusted = false); + bool is_content_trusted = false, bool skip_non_mentions = false); /// a slightly less efficient version of findUrl for wide strings bool findUrl(const LLWString &text, LLUrlMatch &match, @@ -101,6 +101,7 @@ private: LLUrlEntryBase* mUrlEntrySLLabel; LLUrlEntryBase* mUrlEntryNoLink; LLUrlEntryBase* mUrlEntryKeybinding; + LLUrlEntryBase* mUrlEntryAgentMention; // Normalize only trusted URL LLUrlEntryBase* mUrlEntryTrustedUrl; // Wear folder SLUrl diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 92061da24e..2a5d70d147 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -319,6 +319,7 @@ set(viewer_SOURCE_FILES llfloatercamera.cpp llfloatercamerapresets.cpp llfloaterchangeitemthumbnail.cpp + llfloaterchatmentionpicker.cpp llfloaterchatvoicevolume.cpp llfloaterclassified.cpp llfloatercolorpicker.cpp @@ -1137,6 +1138,7 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamerapresets.h llfloaterchangeitemthumbnail.h + llfloaterchatmentionpicker.h llfloatercamera.h llfloaterchatvoicevolume.h llfloaterclassified.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 991d8e5c5f..099f298456 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.1.13 +7.1.14 diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp index 136a626252..3ff298a03c 100644 --- a/indra/newview/fsfloaterim.cpp +++ b/indra/newview/fsfloaterim.cpp @@ -53,6 +53,7 @@ #include "llconsole.h" #include "llfloaterabout.h" // for sysinfo button -Zi #include "llfloateravatarpicker.h" +#include "llfloaterchatmentionpicker.h" #include "llfloateremojipicker.h" #include "llfloaterreg.h" #include "llfloatersearchreplace.h" @@ -205,6 +206,8 @@ void FSFloaterIM::onFocusLost() LLIMModel::getInstance()->resetActiveSessionID(); LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false); + + LLTransientDockableFloater::onFocusLost(); } void FSFloaterIM::onFocusReceived() @@ -217,6 +220,10 @@ void FSFloaterIM::onFocusReceived() { LLIMModel::instance().sendNoUnreadMessages(mSessionID); } + + LLFloaterChatMentionPicker::updateSessionID(mSessionID); + + LLTransientDockableFloater::onFocusReceived(); } // virtual @@ -369,7 +376,7 @@ void FSFloaterIM::sendMsgFromInputEditor(EChatType type) if (mInputEditor) { - LLWString text = mInputEditor->getWText(); + LLWString text = mInputEditor->getConvertedText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) diff --git a/indra/newview/fsfloaternearbychat.cpp b/indra/newview/fsfloaternearbychat.cpp index a39f392acb..e7715d43aa 100644 --- a/indra/newview/fsfloaternearbychat.cpp +++ b/indra/newview/fsfloaternearbychat.cpp @@ -50,6 +50,7 @@ #include "llcommandhandler.h" #include "llconsole.h" #include "lldraghandle.h" +#include "llfloaterchatmentionpicker.h" #include "llfloateremojipicker.h" #include "llfloaterreg.h" #include "llfloatersearchreplace.h" @@ -796,7 +797,7 @@ void FSFloaterNearbyChat::sendChat( EChatType type ) { if (mInputEditor) { - LLWString text = mInputEditor->getWText(); + LLWString text = mInputEditor->getConvertedText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) @@ -1036,3 +1037,11 @@ void FSFloaterNearbyChat::onEmojiPickerToggleBtnClicked() mInputEditor->setFocus(true); mInputEditor->showEmojiHelper(); } + + +void FSFloaterNearbyChat::onFocusReceived() +{ + LLFloaterChatMentionPicker::updateSessionID(LLUUID::null); + + LLFloater::onFocusReceived(); +} diff --git a/indra/newview/fsfloaternearbychat.h b/indra/newview/fsfloaternearbychat.h index 2a7f2e5d24..952ee25f78 100644 --- a/indra/newview/fsfloaternearbychat.h +++ b/indra/newview/fsfloaternearbychat.h @@ -127,6 +127,8 @@ private: void initEmojiRecentPanel(); void onRecentEmojiPicked(const LLSD& value); + void onFocusReceived(); + FSChatHistory* mChatHistory; FSChatHistory* mChatHistoryMuted; LLChatEntry* mInputEditor; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 83782071b8..150d251f62 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -544,7 +544,7 @@ static void deferred_ui_audio_callback(const LLUUID& uuid) bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) { - if(!match || !base || base->getPlainText()) + if (!match || match->getSkipProfileIcon() || !base || base->getPlainText()) return false; LLUUID match_id = match->getID(); diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index d7cf9b5868..fb781a635d 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -186,6 +186,7 @@ LLAvatarList::LLAvatarList(const Params& p) , mShowSpeakingIndicator(p.show_speaking_indicator) , mShowPermissions(p.show_permissions_granted) , mShowCompleteName(false) +, mForceCompleteName(false) // [RLVa:KB] - Checked: RLVa-1.2.0 , mRlvCheckShowNames(false) // [/RLVa:KB] @@ -254,7 +255,7 @@ void LLAvatarList::setShowIcons(std::string param_name) std::string LLAvatarList::getAvatarName(LLAvatarName av_name) { - return mShowCompleteName? av_name.getCompleteName(false) : av_name.getDisplayName(); + return mShowCompleteName? av_name.getCompleteName(false, mForceCompleteName) : av_name.getDisplayName(); } // Update voice volume slider on RLVa shownames restriction update @@ -389,7 +390,7 @@ void LLAvatarList::refresh() // FIRE-12750: Name filter not working correctly //if (!have_filter || findInsensitive(getAvatarName(av_name), mNameFilter)) - if (!have_filter || findInsensitive(getNameForDisplay(buddy_id, av_name, mShowDisplayName, mShowUsername, mRlvCheckShowNames), mNameFilter)) + if (!have_filter || findInsensitive(getNameForDisplay(buddy_id, av_name, mShowDisplayName, mShowUsername, mForceCompleteName, mRlvCheckShowNames), mNameFilter)) // { if (nadded >= ADD_LIMIT) @@ -438,7 +439,7 @@ void LLAvatarList::refresh() have_names &= LLAvatarNameCache::get(buddy_id, &av_name); // FIRE-12750: Name filter not working correctly //if (!findInsensitive(getAvatarName(av_name), mNameFilter)) - if (!findInsensitive(getNameForDisplay(buddy_id, av_name, mShowDisplayName, mShowUsername, mRlvCheckShowNames), mNameFilter)) + if (!findInsensitive(getNameForDisplay(buddy_id, av_name, mShowDisplayName, mShowUsername, mForceCompleteName, mRlvCheckShowNames), mNameFilter)) // { removeItemByUUID(buddy_id); @@ -493,7 +494,7 @@ void LLAvatarList::updateAvatarNames() for( std::vector::const_iterator it = items.begin(); it != items.end(); it++) { LLAvatarListItem* item = static_cast(*it); - item->setShowCompleteName(mShowCompleteName); + item->setShowCompleteName(mShowCompleteName, mForceCompleteName); item->updateAvatarName(); } mNeedUpdateNames = false; @@ -515,7 +516,7 @@ bool LLAvatarList::filterHasMatches() // FIRE-12750: Name filter not working correctly //if (have_name && !findInsensitive(getAvatarName(av_name), mNameFilter)) - if (have_name && !findInsensitive(getNameForDisplay(buddy_id, av_name, mShowDisplayName, mShowUsername, mRlvCheckShowNames), mNameFilter)) + if (have_name && !findInsensitive(getNameForDisplay(buddy_id, av_name, mShowDisplayName, mShowUsername, mForceCompleteName, mRlvCheckShowNames), mNameFilter)) // { continue; @@ -536,6 +537,11 @@ boost::signals2::connection LLAvatarList::setItemDoubleClickCallback(const mouse return mItemDoubleClickSignal.connect(cb); } +boost::signals2::connection LLAvatarList::setItemClickedCallback(const mouse_signal_t::slot_type& cb) +{ + return mItemClickedSignal.connect(cb); +} + //virtual S32 LLAvatarList::notifyParent(const LLSD& info) { @@ -570,7 +576,7 @@ S32 LLAvatarList::notifyParent(const LLSD& info) void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is_online, EAddPosition pos) { LLAvatarListItem* item = new LLAvatarListItem(); - item->setShowCompleteName(mShowCompleteName); + item->setShowCompleteName(mShowCompleteName, mForceCompleteName); // [RLVa:KB] - Checked: RLVa-1.2.0 item->setRlvCheckShowNames(mRlvCheckShowNames); // [/RLVa:KB] @@ -590,6 +596,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is item->showDisplayName(mShowDisplayName); item->setDoubleClickCallback(boost::bind(&LLAvatarList::onItemDoubleClicked, this, _1, _2, _3, _4)); + item->setMouseDownCallback(boost::bind(&LLAvatarList::onItemClicked, this, _1, _2, _3, _4)); addItem(item, id, pos); } @@ -715,9 +722,14 @@ void LLAvatarList::onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) // [/RLVa:KB] } +void LLAvatarList::onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + mItemClickedSignal(ctrl, x, y, mask); +} + // FIRE-12750: Name filter not working correctly // static -std::string LLAvatarList::getNameForDisplay(const LLUUID& avatar_id, const LLAvatarName& av_name, bool show_displayname, bool show_username, bool rlv_check_shownames) +std::string LLAvatarList::getNameForDisplay(const LLUUID& avatar_id, const LLAvatarName& av_name, bool show_displayname, bool show_username, bool force_use_complete_name, bool rlv_check_shownames) { bool fRlvCanShowName = (!rlv_check_shownames) || (RlvActions::canShowName(RlvActions::SNC_DEFAULT, avatar_id)); if (show_displayname && !show_username) @@ -730,7 +742,7 @@ std::string LLAvatarList::getNameForDisplay(const LLUUID& avatar_id, const LLAva } else { - return ( (fRlvCanShowName) ? av_name.getCompleteName() : RlvStrings::getAnonym(av_name) ); + return ( (fRlvCanShowName) ? av_name.getCompleteName(false, force_use_complete_name) : RlvStrings::getAnonym(av_name) ); } } // diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 0b50ffdc35..fecad4a080 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -114,17 +114,19 @@ public: // [/FS:CR] // FIRE-12750: Name filter not working correctly - static std::string getNameForDisplay(const LLUUID& avatar_id, const LLAvatarName& av_name, bool show_displayname, bool show_username, bool rlv_check_shownames); + static std::string getNameForDisplay(const LLUUID& avatar_id, const LLAvatarName& av_name, bool show_displayname, bool show_username, bool force_use_complete_name, bool rlv_check_shownames); boost::signals2::connection setRefreshCompleteCallback(const commit_signal_t::slot_type& cb); boost::signals2::connection setItemDoubleClickCallback(const mouse_signal_t::slot_type& cb); + boost::signals2::connection setItemClickedCallback(const mouse_signal_t::slot_type& cb); + virtual S32 notifyParent(const LLSD& info); void handleDisplayNamesOptionChanged(); - void setShowCompleteName(bool show) { mShowCompleteName = show;}; + void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force; }; protected: void refresh(); @@ -137,7 +139,8 @@ protected: void updateLastInteractionTimes(); void rebuildNames(); void onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); -// void updateAvatarNames(); + void onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); +// void updateAvatarNames(); private: @@ -152,6 +155,7 @@ private: bool mShowSpeakingIndicator; bool mShowPermissions; bool mShowCompleteName; + bool mForceCompleteName; // [RLVa:KB] - RLVa-1.2.0 bool mRlvCheckShowNames; // [/RLVa:KB] @@ -168,6 +172,7 @@ private: commit_signal_t mRefreshCompleteSignal; mouse_signal_t mItemDoubleClickSignal; + mouse_signal_t mItemClickedSignal; // Update voice volume slider on RLVa shownames restriction update boost::signals2::connection mRlvBehaviorCallbackConnection; diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index cf68936541..ccf73cdbf6 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -93,6 +93,7 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) // [/RLVa:KB] mShowPermissions(false), mShowCompleteName(false), + mForceCompleteName(false), mHovered(false), mAvatarNameCacheConnection(), mGreyOutUsername(""), @@ -461,13 +462,11 @@ void LLAvatarListItem::setShowProfileBtn(bool show) void LLAvatarListItem::showSpeakingIndicator(bool visible) { - // Already done? Then do nothing. - if (mSpeakingIndicator->getVisible() == (bool)visible) - return; -// Disabled to not contradict with SpeakingIndicatorManager functionality. EXT-3976 -// probably this method should be totally removed. -// mSpeakingIndicator->setVisible(visible); -// updateChildren(); + if (mSpeakingIndicator) + { + mSpeakingIndicator->setIsActiveChannel(visible); + mSpeakingIndicator->setShowParticipantsSpeaking(visible); + } } void LLAvatarListItem::setAvatarIconVisible(bool visible) @@ -594,8 +593,8 @@ void LLAvatarListItem::onAvatarNameCache(const LLAvatarName& av_name) { mAvatarNameCacheConnection.disconnect(); //mGreyOutUsername = ""; - //std::string name_string = mShowCompleteName? av_name.getCompleteName(false) : av_name.getDisplayName(); - //if(av_name.getCompleteName() != av_name.getUserName()) + //std::string name_string = mShowCompleteName? av_name.getCompleteName(false, mForceCompleteName) : av_name.getDisplayName(); + //if(av_name.getCompleteName(false, mForceCompleteName) != av_name.getUserName()) //{ // mGreyOutUsername = "[ " + av_name.getUserName(true) + " ]"; // LLStringUtil::toLower(mGreyOutUsername); @@ -610,8 +609,8 @@ void LLAvatarListItem::onAvatarNameCache(const LLAvatarName& av_name) //else if (!mShowDisplayName && mShowUsername) // setAvatarName( (fRlvCanShowName) ? av_name.getUserName() : RlvStrings::getAnonym(av_name) ); //else - // setAvatarName( (fRlvCanShowName) ? av_name.getCompleteName() : RlvStrings::getAnonym(av_name) ); - setAvatarName(LLAvatarList::getNameForDisplay(mAvatarId, av_name, mShowDisplayName, mShowUsername, mRlvCheckShowNames)); + // setAvatarName( (fRlvCanShowName) ? av_name.getCompleteName(false, mForceCompleteName) : RlvStrings::getAnonym(av_name) ); + setAvatarName(LLAvatarList::getNameForDisplay(mAvatarId, av_name, mShowDisplayName, mShowUsername, mForceCompleteName, mRlvCheckShowNames)); // NOTE: If you change this, you will break sorting the contacts list // by username unless you go change the comparator too. -- TS diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index d6ec559a89..b12e426f74 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -116,7 +116,7 @@ public: void setShowPermissions(bool show); void showLastInteractionTime(bool show); void setAvatarIconVisible(bool visible); - void setShowCompleteName(bool show) { mShowCompleteName = show;}; + void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force;}; // [RLVa:KB] - Checked: RLVa-1.2.0 void setRlvCheckShowNames(bool fRlvCheckShowNames); void updateRlvRestrictions(); @@ -265,6 +265,7 @@ private: bool mHovered; bool mShowCompleteName; + bool mForceCompleteName; std::string mGreyOutUsername; void fetchAvatarName(); diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h index 5380463d01..23cf253b6a 100644 --- a/indra/newview/lldrawpoolterrain.h +++ b/indra/newview/lldrawpoolterrain.h @@ -38,6 +38,7 @@ public: VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TANGENT | // Only PBR terrain uses this currently + LLVertexBuffer::MAP_TEXCOORD0 | // Ownership overlay LLVertexBuffer::MAP_TEXCOORD1 }; diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index bb73db96ef..8de6c4075f 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -2711,7 +2711,6 @@ void LLEnvironment::setSharedEnvironment() { clearEnvironment(LLEnvironment::ENV_LOCAL); setSelectedEnvironment(LLEnvironment::ENV_LOCAL); - updateEnvironment(); } void LLEnvironment::setExperienceEnvironment(LLUUID experience_id, LLUUID asset_id, F32 transition_time) diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp index c09c02d32b..74c5079268 100644 --- a/indra/newview/llfloaterbulkpermission.cpp +++ b/indra/newview/llfloaterbulkpermission.cpp @@ -89,9 +89,17 @@ bool LLFloaterBulkPermission::postBuild() { mBulkChangeNextOwnerTransfer = true; } + + mQueueOutputList = getChild("queue output"); return true; } +void LLFloaterBulkPermission::onClose(bool app_quitting) +{ + removeVOInventoryListener(); + LLFloater::onClose(app_quitting); +} + void LLFloaterBulkPermission::doApply() { // Inspects a stream of selected object contents and adds modifiable ones to the given array. @@ -216,7 +224,7 @@ void LLFloaterBulkPermission::onCommitCopy() bool LLFloaterBulkPermission::start() { // note: number of top-level objects to modify is mObjectIDs.size(). - getChild("queue output")->setCommentText(getString("start_text")); + mQueueOutputList->setCommentText(getString("start_text")); return nextObject(); } @@ -239,7 +247,7 @@ bool LLFloaterBulkPermission::nextObject() if(isDone() && !mDone) { - getChild("queue output")->setCommentText(getString("done_text")); + mQueueOutputList->setCommentText(getString("done_text")); mDone = true; } return successful_start; @@ -294,8 +302,6 @@ void LLFloaterBulkPermission::doCheckUncheckAll(bool check) void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInventoryObject::object_list_t* inv) { - LLScrollListCtrl* list = getChild("queue output"); - LLInventoryObject::object_list_t::const_iterator it = inv->begin(); LLInventoryObject::object_list_t::const_iterator end = inv->end(); for ( ; it != end; ++it) @@ -362,7 +368,7 @@ void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInve status_text.setArg("[STATUS]", ""); } - list->setCommentText(status_text.getString()); + mQueueOutputList->setCommentText(status_text.getString()); //TODO if we are an object inside an object we should check a recuse flag and if set //open the inventory of the object and recurse - Michelle2 Zenovka diff --git a/indra/newview/llfloaterbulkpermission.h b/indra/newview/llfloaterbulkpermission.h index 23ca45b611..0b61022e0c 100644 --- a/indra/newview/llfloaterbulkpermission.h +++ b/indra/newview/llfloaterbulkpermission.h @@ -41,7 +41,8 @@ class LLFloaterBulkPermission : public LLFloater, public LLVOInventoryListener friend class LLFloaterReg; public: - bool postBuild(); + bool postBuild() override; + void onClose(bool app_quitting) override; private: @@ -57,7 +58,7 @@ private: /*virtual*/ void inventoryChanged(LLViewerObject* obj, LLInventoryObject::object_list_t* inv, S32 serial_num, - void* queue); + void* queue) override; // This is called by inventoryChanged void handleInventory(LLViewerObject* viewer_obj, @@ -85,7 +86,7 @@ private: private: // UI - LLScrollListCtrl* mMessages; + LLScrollListCtrl* mQueueOutputList = nullptr; LLButton* mCloseBtn; // Object Queue diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp new file mode 100644 index 0000000000..4265e73f0d --- /dev/null +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -0,0 +1,189 @@ +/** + * @file llfloaterchatmentionpicker.cpp + * + * $LicenseInfo:firstyear=2025&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2025, 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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterchatmentionpicker.h" + +#include "llavatarlist.h" +#include "llfloaterimcontainer.h" +#include "llchatmentionhelper.h" +#include "llparticipantlist.h" + +LLUUID LLFloaterChatMentionPicker::sSessionID(LLUUID::null); + +LLFloaterChatMentionPicker::LLFloaterChatMentionPicker(const LLSD& key) +: LLFloater(key), mAvatarList(NULL) +{ + // This floater should hover on top of our dependent (with the dependent having the focus) + setFocusStealsFrontmost(false); + setBackgroundVisible(false); + setAutoFocus(false); +} + +bool LLFloaterChatMentionPicker::postBuild() +{ + mAvatarList = getChild("avatar_list"); + mAvatarList->setShowCompleteName(true, true); + mAvatarList->setFocusOnItemClicked(false); + mAvatarList->setItemClickedCallback([this](LLUICtrl* ctrl, S32 x, S32 y, MASK mask) + { + if (LLAvatarListItem* item = dynamic_cast(ctrl)) + { + selectResident(item->getAvatarId()); + } + }); + mAvatarList->setRefreshCompleteCallback([this](LLUICtrl* ctrl, const LLSD& param) + { + if (mAvatarList->numSelected() == 0) + { + mAvatarList->selectFirstItem(); + } + }); + + return LLFloater::postBuild(); +} + +void LLFloaterChatMentionPicker::onOpen(const LLSD& key) +{ + buildAvatarList(); + mAvatarList->setNameFilter(key.has("av_name") ? key["av_name"].asString() : ""); + + gFloaterView->adjustToFitScreen(this, false); +} + +uuid_vec_t LLFloaterChatMentionPicker::getParticipantIds() +{ + /* + LLParticipantList* item = dynamic_cast(LLFloaterIMContainer::getInstance()->getSessionModel(sSessionID)); + if (!item) + { + LL_WARNS() << "Participant list is missing" << LL_ENDL; + return {}; + } + + uuid_vec_t avatar_ids; + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + if (participant_model) + { + avatar_ids.push_back(participant_model->getUUID()); + } + current_participant_model++; + } + return avatar_ids; + */ + return{}; +} + +void LLFloaterChatMentionPicker::buildAvatarList() +{ + uuid_vec_t& avatar_ids = mAvatarList->getIDs(); + avatar_ids = getParticipantIds(); + updateAvatarList(avatar_ids); + mAvatarList->setDirty(); +} + +void LLFloaterChatMentionPicker::selectResident(const LLUUID& id) +{ + if (id.isNull()) + return; + + setValue(stringize("secondlife:///app/agent/", id.asString(), "/mention ")); + onCommit(); + LLChatMentionHelper::instance().hideHelper(); +} + +void LLFloaterChatMentionPicker::onClose(bool app_quitting) +{ + if (!app_quitting) + { + LLChatMentionHelper::instance().hideHelper(); + } +} + +bool LLFloaterChatMentionPicker::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + if (mask == MASK_NONE) + { + switch (key) + { + case KEY_UP: + case KEY_DOWN: + return mAvatarList->handleKey(key, mask, called_from_parent); + case KEY_RETURN: + case KEY_TAB: + selectResident(mAvatarList->getSelectedUUID()); + return true; + case KEY_ESCAPE: + LLChatMentionHelper::instance().hideHelper(); + return true; + case KEY_LEFT: + case KEY_RIGHT: + return true; + default: + break; + } + } + return LLFloater::handleKey(key, mask, called_from_parent); +} + +void LLFloaterChatMentionPicker::goneFromFront() +{ + LLChatMentionHelper::instance().hideHelper(); +} + +void LLFloaterChatMentionPicker::updateSessionID(LLUUID session_id) +{ + sSessionID = session_id; + + /* + LLParticipantList* item = dynamic_cast(LLFloaterIMContainer::getInstance()->getSessionModel(sSessionID)); + if (!item) + { + LL_WARNS() << "Participant list is missing" << LL_ENDL; + return; + } + */ + + uuid_vec_t avatar_ids = getParticipantIds(); + updateAvatarList(avatar_ids); +} + +void LLFloaterChatMentionPicker::updateAvatarList(uuid_vec_t& avatar_ids) +{ + std::vector av_names; + for (auto& id : avatar_ids) + { + LLAvatarName av_name; + LLAvatarNameCache::get(id, &av_name); + av_names.push_back(utf8str_tolower(av_name.getAccountName())); + av_names.push_back(utf8str_tolower(av_name.getDisplayName())); + } + LLChatMentionHelper::instance().updateAvatarList(av_names); +} diff --git a/indra/newview/llfloaterchatmentionpicker.h b/indra/newview/llfloaterchatmentionpicker.h new file mode 100644 index 0000000000..8d221d7a89 --- /dev/null +++ b/indra/newview/llfloaterchatmentionpicker.h @@ -0,0 +1,58 @@ +/** + * @file llfloaterchatmentionpicker.h + * + * $LicenseInfo:firstyear=2025&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2025, 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 LLFLOATERCHATMENTIONPICKER_H +#define LLFLOATERCHATMENTIONPICKER_H + +#include "llfloater.h" + +class LLAvatarList; + +class LLFloaterChatMentionPicker : public LLFloater +{ +public: + LLFloaterChatMentionPicker(const LLSD& key); + + virtual bool postBuild() override; + virtual void goneFromFront() override; + + void buildAvatarList(); + + static uuid_vec_t getParticipantIds(); + static void updateSessionID(LLUUID session_id); + static void updateAvatarList(uuid_vec_t& avatar_ids); + +private: + + void onOpen(const LLSD& key) override; + void onClose(bool app_quitting) override; + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent) override; + void selectResident(const LLUUID& id); + + static LLUUID sSessionID; + LLAvatarList* mAvatarList; +}; + +#endif diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index 42307dd3f8..0a8b8d321d 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -495,7 +495,6 @@ void LLFloaterEditExtDayCycle::setEditDayCycle(const LLSettingsDay::ptr_t &pday) updateEditEnvironment(); LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_INSTANT); - LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_INSTANT); synchronizeTabs(); updateTabs(); refresh(); @@ -824,7 +823,6 @@ void LLFloaterEditExtDayCycle::onClearTrack() updateEditEnvironment(); LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_INSTANT); - LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_INSTANT); synchronizeTabs(); updateTabs(); refresh(); diff --git a/indra/newview/llfloaterenvironmentadjust.cpp b/indra/newview/llfloaterenvironmentadjust.cpp index 58616995d3..4825cbf7fb 100644 --- a/indra/newview/llfloaterenvironmentadjust.cpp +++ b/indra/newview/llfloaterenvironmentadjust.cpp @@ -242,9 +242,7 @@ void LLFloaterEnvironmentAdjust::captureCurrentEnvironment() environment.setEnvironment(LLEnvironment::ENV_LOCAL, mLiveSky, FLOATER_ENVIRONMENT_UPDATE); environment.setEnvironment(LLEnvironment::ENV_LOCAL, mLiveWater, FLOATER_ENVIRONMENT_UPDATE); } - environment.setSelectedEnvironment(LLEnvironment::ENV_LOCAL); - environment.updateEnvironment(LLEnvironment::TRANSITION_INSTANT); - + environment.setSelectedEnvironment(LLEnvironment::ENV_LOCAL, LLEnvironment::TRANSITION_INSTANT); } void LLFloaterEnvironmentAdjust::onButtonReset() @@ -258,7 +256,6 @@ void LLFloaterEnvironmentAdjust::onButtonReset() this->closeFloater(); LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_LOCAL); LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); - LLEnvironment::instance().updateEnvironment(); } }); diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index acd236dd6a..8f7fbee66e 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -596,7 +596,7 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) { if (mInputEditor) { - LLWString text = mInputEditor->getWText(); + LLWString text = mInputEditor->getConvertedText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index ea8dfff3f9..05a70f049f 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -256,7 +256,7 @@ void LLFloaterIMSession::sendMsgFromInputEditor() { if (mInputEditor) { - LLWString text = mInputEditor->getWText(); + LLWString text = mInputEditor->getConvertedText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if(!text.empty()) diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index b09f5a4b64..821c1fa2e2 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -37,6 +37,7 @@ #include "llavatariconctrl.h" #include "llchatentry.h" #include "llchathistory.h" +#include "llfloaterchatmentionpicker.h" #include "llchiclet.h" #include "llchicletbar.h" #include "lldraghandle.h" @@ -487,6 +488,7 @@ void LLFloaterIMSessionTab::onFocusReceived() LLIMModel::instance().sendNoUnreadMessages(mSessionID); } + LLFloaterChatMentionPicker::updateSessionID(mSessionID); super::onFocusReceived(); } diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp index 9ea51451ad..1badcdd3d9 100644 --- a/indra/newview/llfloaternewfeaturenotification.cpp +++ b/indra/newview/llfloaternewfeaturenotification.cpp @@ -43,29 +43,28 @@ bool LLFloaterNewFeatureNotification::postBuild() setCanDrag(false); getChild("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this)); - const std::string title_txt = "title_txt"; - const std::string dsc_txt = "description_txt"; - // FIRE-35393 stop crashing just cos whirly does something daft and blames Atlas for it - std::string feature = getKey().asString(); - if(feature.empty() ) + if (getKey().isString()) { - LL_WARNS("FloaterNewFeature") << "Unexpected failure - No feature name NewFeatureNotification." << LL_ENDL; - return false; + const std::string title_txt = "title_txt"; + const std::string dsc_txt = "description_txt"; + + std::string feature = "_" + getKey().asString(); + if (hasString(title_txt + feature)) + { + getChild(title_txt)->setValue(getString(title_txt + feature)); + getChild(dsc_txt)->setValue(getString(dsc_txt + feature)); + } + else + { + // Show blank + LL_WARNS() << "Feature \"" << getKey().asString() << "\" not found for feature notification" << LL_ENDL; + } } - if (!hasString( title_txt + "_" + feature ) ) + else { - LL_WARNS("FloaterNewFeature") << "No string for " << title_txt + "_" + feature << LL_ENDL; - return false; + // Show blank + LL_WARNS() << "Feature notification without a feature" << LL_ENDL; } - if (!hasString( dsc_txt + "_" + feature ) ) - { - LL_WARNS("FloaterNewFeature") << "No string for " << dsc_txt + "_" + feature << LL_ENDL; - return false; - } - // - - getChild(title_txt)->setValue(getString(title_txt + "_" + feature)); - getChild(dsc_txt)->setValue(getString(dsc_txt + "_" + feature)); if (getKey().asString() == "gltf") { diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 5f78227a57..ecdfe565dd 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3010,7 +3010,7 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, is_movable = false; tooltip_msg = LLTrans::getString("TooltipCantMoveOutfitIntoOutfit"); } - else if ((dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) && inv_res == MY_OUTFITS_SUBFOLDER) + else if (dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) { is_movable = false; tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit"); @@ -4906,7 +4906,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (cat->getPreferredType() == LLFolderType::FT_OUTFIT) { // Want some but not all of the items from getClipboardEntries for outfits. - //items.push_back(std::string("New Outfit Folder")); // Normal subfolder underneath an outfit folder makes no sense items.push_back(std::string("Rename")); items.push_back(std::string("thumbnail")); @@ -4939,9 +4938,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items { // Not inside an outfit, but inside 'my outfits' items.push_back(std::string("New Outfit")); + items.push_back(std::string("New Outfit Folder")); } - - items.push_back(std::string("New Outfit Folder")); items.push_back(std::string("Rename")); items.push_back(std::string("thumbnail")); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 9b5859738d..4a6d74f885 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -3789,7 +3789,7 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, is_movable = false; tooltip_msg = LLTrans::getString("TooltipCantMoveOutfitIntoOutfit"); } - else if ((dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) && inv_res == MY_OUTFITS_SUBFOLDER) + else if (dest_res == MY_OUTFITS_OUTFIT || dest_res == MY_OUTFITS_SUBOUTFIT) { is_movable = false; tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit"); diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index c073f9cbb5..244ae8d6fa 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -789,8 +789,8 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men if (res != MY_OUTFITS_OUTFIT && res != MY_OUTFITS_SUBOUTFIT) { items.push_back(std::string("New Outfit")); + items.push_back(std::string("New Outfit Folder")); } - items.push_back(std::string("New Outfit Folder")); items.push_back(std::string("Delete")); items.push_back(std::string("Rename")); if (!get_is_category_and_children_removable(&gInventory, selected_id, false)) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 428da07414..8bfcf135fd 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -560,6 +560,66 @@ bool RequestStats::isDelayed() const return mTimer.getStarted() && !mTimer.hasExpired(); } +F32 calculate_score(LLVOVolume* object) +{ + if (!object) + { + return -1.f; + } + LLDrawable* drawable = object->mDrawable; + if (!drawable) + { + return -1; + } + if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) + { + LLVOAvatar* avatar = object->getAvatar(); + LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; + if (avatar && av_drawable) + { + // See LLVOVolume::calcLOD() + F32 radius; + if (avatar->isControlAvatar()) + { + const LLVector3* box = avatar->getLastAnimExtents(); + LLVector3 diag = box[1] - box[0]; + radius = diag.magVec() * 0.5f; + } + else + { + // Volume in a rigged mesh attached to a regular avatar. + const LLVector3* box = avatar->getLastAnimExtents(); + LLVector3 diag = box[1] - box[0]; + radius = diag.magVec(); + + if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) + { + // slightly deprioritize avatars that are still receiving data + radius *= 0.9f; + } + } + return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); + } + } + return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); +} + +void PendingRequestBase::updateScore() +{ + mScore = 0; + if (mTrackedData) + { + for (LLVOVolume* volume : mTrackedData->mVolumes) + { + F32 cur_score = calculate_score(volume); + if (cur_score > 0) + { + mScore = llmax(mScore, cur_score); + } + } + } +} + LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material) { LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData); @@ -2327,7 +2387,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) { - gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info + gMeshRepo.mLoadingSkins[mesh_id]; // add an empty vector to indicate to main thread that we are loading skin info } } @@ -4320,13 +4380,13 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) { for (auto& param : lod) { - vector_replace_with_last(param.second, vobj); + vector_replace_with_last(param.second.mVolumes, vobj); } } for (auto& skin_pair : mLoadingSkins) { - vector_replace_with_last(skin_pair.second, vobj); + vector_replace_with_last(skin_pair.second.mVolumes, vobj); } } @@ -4349,16 +4409,17 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id); if (iter != mLoadingMeshes[new_lod].end()) { //request pending for this mesh, append volume id to list - auto it = std::find(iter->second.begin(), iter->second.end(), vobj); - if (it == iter->second.end()) { - iter->second.push_back(vobj); + auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), vobj); + if (it == iter->second.mVolumes.end()) { + iter->second.addVolume(vobj); } } else { //first request for this mesh - mLoadingMeshes[new_lod][mesh_id].push_back(vobj); - mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod)); + std::shared_ptr request(new PendingRequestLOD(mesh_params, new_lod)); + mPendingRequests.emplace_back(request); + mLoadingMeshes[new_lod][mesh_id].initData(vobj, request); LLMeshRepository::sLODPending++; } } @@ -4417,50 +4478,6 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para return new_lod; } -F32 calculate_score(LLVOVolume* object) -{ - if (!object) - { - return -1.f; - } - LLDrawable* drawable = object->mDrawable; - if (!drawable) - { - return -1; - } - if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) - { - LLVOAvatar* avatar = object->getAvatar(); - LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; - if (avatar && av_drawable) - { - // See LLVOVolume::calcLOD() - F32 radius; - if (avatar->isControlAvatar()) - { - const LLVector3* box = avatar->getLastAnimExtents(); - LLVector3 diag = box[1] - box[0]; - radius = diag.magVec() * 0.5f; - } - else - { - // Volume in a rigged mesh attached to a regular avatar. - const LLVector3* box = avatar->getLastAnimExtents(); - LLVector3 diag = box[1] - box[0]; - radius = diag.magVec(); - - if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) - { - // slightly deprioritize avatars that are still receiving data - radius *= 0.9f; - } - } - return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); - } - } - return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); -} - void LLMeshRepository::notifyLoadedMeshes() { //called from main thread LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); @@ -4727,51 +4744,14 @@ void LLMeshRepository::notifyLoadedMeshes() if (mPendingRequests.size() > push_count) { - LL_PROFILE_ZONE_NAMED("Mesh score_map"); + LL_PROFILE_ZONE_NAMED("Mesh score update"); // More requests than the high-water limit allows so // sort and forward the most important. - //calculate "score" for pending requests - - //create score map - std::map score_map; - - for (U32 i = 0; i < LLVolumeLODGroup::NUM_LODS; ++i) + // update "score" for pending requests + for (std::shared_ptr& req_p : mPendingRequests) { - for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) - { - F32 max_score = 0.f; - for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) - { - F32 cur_score = calculate_score(*obj_iter); - if (cur_score >= 0.f) - { - max_score = llmax(max_score, cur_score); - } - } - - score_map[iter->first] = max_score; - } - } - for (mesh_load_map::iterator iter = mLoadingSkins.begin(); iter != mLoadingSkins.end(); ++iter) - { - F32 max_score = 0.f; - for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) - { - F32 cur_score = calculate_score(*obj_iter); - if (cur_score >= 0.f) - { - max_score = llmax(max_score, cur_score); - } - } - - score_map[iter->first] = max_score; - } - - //set "score" for pending requests - for (std::unique_ptr& req_p : mPendingRequests) - { - req_p->setScore(score_map[req_p->getId()]); + req_p->checkScore(); } //sort by "score" @@ -4780,7 +4760,9 @@ void LLMeshRepository::notifyLoadedMeshes() } while (!mPendingRequests.empty() && push_count > 0) { - std::unique_ptr& req_p = mPendingRequests.front(); + std::shared_ptr& req_p = mPendingRequests.front(); + // todo: check hasTrackedData here and erase request if none + // since this is supposed to mean that request was removed switch (req_p->getRequestType()) { case MESH_REQUEST_LOD: @@ -4835,7 +4817,7 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info) skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID); if (iter != mLoadingSkins.end()) { - for (LLVOVolume* vobj : iter->second) + for (LLVOVolume* vobj : iter->second.mVolumes) { if (vobj) { @@ -4851,7 +4833,7 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id) skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); if (iter != mLoadingSkins.end()) { - for (LLVOVolume* vobj : iter->second) + for (LLVOVolume* vobj : iter->second.mVolumes) { if (vobj) { @@ -4915,7 +4897,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } //notify waiting LLVOVolume instances that their requested mesh is available - for (LLVOVolume* vobj : obj_iter->second) + for (LLVOVolume* vobj : obj_iter->second.mVolumes) { if (vobj) { @@ -4945,7 +4927,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); } - for (LLVOVolume* vobj : obj_iter->second) + for (LLVOVolume* vobj : obj_iter->second.mVolumes) { if (vobj) { @@ -4988,16 +4970,17 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); if (iter != mLoadingSkins.end()) { //request pending for this mesh, append volume id to list - auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj); - if (it == iter->second.end()) { - iter->second.push_back(requesting_obj); + auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), requesting_obj); + if (it == iter->second.mVolumes.end()) { + iter->second.addVolume(requesting_obj); } } else { //first request for this mesh - mLoadingSkins[mesh_id].push_back(requesting_obj); - mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); + std::shared_ptr request(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); + mLoadingSkins[mesh_id].initData(requesting_obj, request); + mPendingRequests.emplace_back(request); } } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 4e91f82ed6..5281b8fea8 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -168,7 +168,6 @@ public: void submitRequest(Request* request); static S32 llcdCallback(const char*, S32, S32); - void cancel(); void setMeshData(LLCDMeshData& mesh, bool vertex_based); void doDecomposition(); @@ -206,19 +205,19 @@ private: LLFrameTimer mTimer; }; - +class MeshLoadData; class PendingRequestBase { public: struct CompareScoreGreater { - bool operator()(const std::unique_ptr& lhs, const std::unique_ptr& rhs) + bool operator()(const std::shared_ptr& lhs, const std::shared_ptr& rhs) { return lhs->mScore > rhs->mScore; // greatest = first } }; - PendingRequestBase() : mScore(0.f) {}; + PendingRequestBase() : mScore(0.f), mTrackedData(nullptr), mScoreDirty(true) {}; virtual ~PendingRequestBase() {} bool operator<(const PendingRequestBase& rhs) const @@ -226,14 +225,34 @@ public: return mId < rhs.mId; } - void setScore(F32 score) { mScore = score; } F32 getScore() const { return mScore; } + void checkScore() + { + constexpr F32 EXPIRE_TIME_SECS = 8.f; + if (mScoreTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS || mScoreDirty) + { + updateScore(); + mScoreDirty = false; + mScoreTimer.reset(); + } + }; + LLUUID getId() const { return mId; } virtual EMeshRequestType getRequestType() const = 0; + void trackData(MeshLoadData* data) { mTrackedData = data; mScoreDirty = true; } + void untrackData() { mTrackedData = nullptr; } + bool hasTrackedData() { return mTrackedData != nullptr; } + void setScoreDirty() { mScoreDirty = true; } + protected: - F32 mScore; + void updateScore(); + LLUUID mId; + F32 mScore; + bool mScoreDirty; + LLTimer mScoreTimer; + MeshLoadData* mTrackedData; }; class PendingRequestLOD : public PendingRequestBase @@ -267,6 +286,37 @@ private: EMeshRequestType mRequestType; }; + +class MeshLoadData +{ +public: + MeshLoadData() {} + ~MeshLoadData() + { + if (std::shared_ptr request = mRequest.lock()) + { + request->untrackData(); + } + } + void initData(LLVOVolume* vol, std::shared_ptr& request) + { + mVolumes.push_back(vol); + request->trackData(this); + mRequest = request; + } + void addVolume(LLVOVolume* vol) + { + mVolumes.push_back(vol); + if (std::shared_ptr request = mRequest.lock()) + { + request->setScoreDirty(); + } + } + std::vector mVolumes; +private: + std::weak_ptr mRequest; +}; + class LLMeshHeader { public: @@ -845,7 +895,7 @@ public: static void metricsProgress(unsigned int count); static void metricsUpdate(); - typedef std::unordered_map > mesh_load_map; + typedef std::unordered_map mesh_load_map; mesh_load_map mLoadingMeshes[4]; // DAE export @@ -859,11 +909,11 @@ public: LLMutex* mMeshMutex; - typedef std::vector > pending_requests_vec; + typedef std::vector > pending_requests_vec; pending_requests_vec mPendingRequests; //list of mesh ids awaiting skin info - typedef std::unordered_map > skin_load_map; + typedef std::unordered_map skin_load_map; skin_load_map mLoadingSkins; //list of mesh ids awaiting decompositions diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 8979b419cd..cf8cc1cfdb 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -333,8 +333,8 @@ public: const std::string verb = params[1].asString(); // FIRE-9045: Inspect links always open full profile - //if (verb == "about") - if (verb == "about" || (gSavedSettings.getBOOL("FSInspectAvatarSlurlOpensProfile") && verb == "inspect")) + //if (verb == "about" || verb == "mention") + if (verb == "about" || verb == "mention" || (gSavedSettings.getBOOL("FSInspectAvatarSlurlOpensProfile") && verb == "inspect")) // { LLAvatarActions::showProfile(avatar_id); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 85541d8175..0af84eb9ac 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -4590,7 +4590,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y // Agent id needed for parcel info request in LLUrlEntryParcel // to resolve parcel name. - LLUrlEntryParcel::setAgentID(gAgentID); + LLUrlEntryBase::setAgentID(gAgentID); text = response["session_id"].asString(); if(!text.empty()) gAgentSessionID.set(text); diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index c2f8c2b990..342b6dfaa2 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -226,7 +226,7 @@ LLVector2 LLSurfacePatch::getTexCoords(const U32 x, const U32 y) const void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 *vertex, LLVector3 *normal, - LLVector2 *tex1) const + LLVector2* tex0, LLVector2 *tex1) const { if (!mSurfacep || !mSurfacep->getRegion() || !mSurfacep->getGridsPerEdge() || !mVObjp) { @@ -235,6 +235,7 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 llassert_always(vertex && normal && tex1); U32 surface_stride = mSurfacep->getGridsPerEdge(); + U32 texture_stride = mSurfacep->getGridsPerEdge() - 1; U32 point_offset = x + y*surface_stride; *normal = getNormal(x, y); @@ -245,6 +246,12 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 pos_agent.mV[VZ] = *(mDataZ + point_offset); *vertex = pos_agent-mVObjp->getRegion()->getOriginAgent(); + // tex0 is used for ownership overlay + LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent(); + LLVector3 tex_pos = rel_pos * (1.f / (texture_stride * mSurfacep->getMetersPerGrid())); + tex0->mV[0] = tex_pos.mV[0]; + tex0->mV[1] = tex_pos.mV[1]; + tex1->mV[0] = mSurfacep->getRegion()->getCompositionXY(llfloor(mOriginRegion.mV[0])+x, llfloor(mOriginRegion.mV[1])+y); const F32 xyScale = 4.9215f*7.f; //0.93284f; diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h index f4831487c1..505fc8c24c 100644 --- a/indra/newview/llsurfacepatch.h +++ b/indra/newview/llsurfacepatch.h @@ -116,7 +116,7 @@ public: void calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index /* 0 or 1 */); void eval(const U32 x, const U32 y, const U32 stride, - LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex1) const; + LLVector3 *vertex, LLVector3 *normal, LLVector2* tex0, LLVector2 *tex1) const; diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 8ccde74c93..c7a82013e4 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -204,8 +204,9 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& { LLVector3 scratch3; LLVector3 pos3; + LLVector2 tex0_temp; LLVector2 tex1_temp; - patch->eval(i, j, stride, &pos3, &scratch3, &tex1_temp); + patch->eval(i, j, stride, &pos3, &scratch3, &tex0_temp, &tex1_temp); (*pos++).set(pos3.mV[VX], pos3.mV[VY], pos3.mV[VZ]); *tex1++ = tex1_temp; vertex_total++; diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index b633dc4fed..aaf0fb1849 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1352,27 +1352,39 @@ U32 LLTextureCache::openAndReadEntries(std::vector& entries) } for (U32 idx=0; idxread((void*)(&entry), (S32)sizeof(Entry)); - if (bytes_read < sizeof(Entry)) + try { - LL_WARNS() << "Corrupted header entries, failed at " << idx << " / " << num_entries << LL_ENDL; + Entry entry; + S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry)); + if (bytes_read < sizeof(Entry)) + { + LL_WARNS() << "Corrupted header entries, failed at " << idx << " / " << num_entries << LL_ENDL; + return 0; + } + entries.push_back(entry); + // LL_INFOS() << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << LL_ENDL; + if (entry.mImageSize > entry.mBodySize) + { + mHeaderIDMap[entry.mID] = idx; + mTexturesSizeMap[entry.mID] = entry.mBodySize; + mTexturesSizeTotal += entry.mBodySize; + } + else + { + mFreeList.insert(idx); + } + } + catch (std::bad_alloc&) + { + // Too little ram yet very large cache? + // Should this actually crash viewer? + entries.clear(); + LL_WARNS() << "Bad alloc trying to read texture entries from cache, mFreeList: " << (S32)mFreeList.size() + << ", added entries: " << idx << ", total entries: " << num_entries << LL_ENDL; closeHeaderEntriesFile(); purgeAllTextures(false); return 0; } - entries.push_back(entry); -// LL_INFOS() << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << LL_ENDL; - if(entry.mImageSize > entry.mBodySize) - { - mHeaderIDMap[entry.mID] = idx; - mTexturesSizeMap[entry.mID] = entry.mBodySize; - mTexturesSizeTotal += entry.mBodySize; - } - else - { - mFreeList.insert(idx); - } } closeHeaderEntriesFile(); return num_entries; diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp index 7f5ae5e10f..97d1070094 100644 --- a/indra/newview/llviewerchat.cpp +++ b/indra/newview/llviewerchat.cpp @@ -36,6 +36,7 @@ #include "llviewerregion.h" #include "llworld.h" #include "llinstantmessage.h" //SYSTEM_FROM +#include "llurlregistry.h" #include "fskeywords.h" #include "lggcontactsets.h" #include "rlvhandler.h" @@ -300,6 +301,13 @@ void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg) { std::string tmpmsg = chat.mText; + // show @name instead of slurl for chat mentions + LLUrlMatch match; + while (LLUrlRegistry::instance().findUrl(tmpmsg, match, LLUrlRegistryNullCallback, false, true)) + { + tmpmsg.replace(match.getStart(), match.getEnd() - match.getStart() + 1, match.getLabel()); + } + if(chat.mChatStyle == CHAT_STYLE_IRC) { formated_msg = chat.mFromName + tmpmsg.substr(3); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 4fc57172d6..8648338468 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -58,6 +58,7 @@ #include "llfloatercamera.h" #include "llfloatercamerapresets.h" #include "llfloaterchangeitemthumbnail.h" +#include "llfloaterchatmentionpicker.h" #include "llfloaterchatvoicevolume.h" #include "llfloaterclassified.h" #include "llfloaterconversationlog.h" @@ -426,6 +427,7 @@ void LLViewerFloaterReg::registerFloaters() //LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); LLFloaterReg::add("fs_nearby_chat", "floater_fs_nearby_chat.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); // [FS communication UI] + LLFloaterReg::add("chat_mention_picker", "floater_chat_mention_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 83e94c7e5b..0c1f448ed0 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -245,6 +245,7 @@ bool LLVOSurfacePatch::updateLOD() void LLVOSurfacePatch::getTerrainGeometry(LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp) { @@ -259,18 +260,21 @@ void LLVOSurfacePatch::getTerrainGeometry(LLStrider &verticesp, updateMainGeometry(facep, verticesp, normalsp, + texCoords0p, texCoords1p, indicesp, index_offset); updateNorthGeometry(facep, verticesp, normalsp, + texCoords0p, texCoords1p, indicesp, index_offset); updateEastGeometry(facep, verticesp, normalsp, + texCoords0p, texCoords1p, indicesp, index_offset); @@ -279,6 +283,7 @@ void LLVOSurfacePatch::getTerrainGeometry(LLStrider &verticesp, void LLVOSurfacePatch::updateMainGeometry(LLFace *facep, LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp, U32 &index_offset) @@ -317,9 +322,10 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep, { x = i * render_stride; y = j * render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } } @@ -381,6 +387,7 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep, void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp, U32 &index_offset) @@ -414,9 +421,10 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, x = i * render_stride; y = 16 - render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -425,9 +433,10 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, { x = i * render_stride; y = 16; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -460,9 +469,10 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, x = i * render_stride; y = 16 - render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -472,9 +482,10 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, x = i * render_stride; y = 16; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -514,9 +525,10 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, x = i * north_stride; y = 16 - render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -526,9 +538,10 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, x = i * north_stride; y = 16; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -564,6 +577,7 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp, U32 &index_offset) @@ -592,9 +606,10 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, x = 16 - render_stride; y = i * render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -603,9 +618,10 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, { x = 16; y = i * render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -638,9 +654,10 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, x = 16 - render_stride; y = i * render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } // Iterate through the east patch's points @@ -649,9 +666,10 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, x = 16; y = i * render_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -690,9 +708,10 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, x = 16 - render_stride; y = i * east_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } // Iterate through the east patch's points @@ -701,9 +720,10 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, x = 16; y = i * east_stride; - mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get()); + mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get()); verticesp++; normalsp++; + texCoords0p++; texCoords1p++; } @@ -1022,12 +1042,14 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group) LLStrider vertices_start; LLStrider normals_start; LLStrider tangents_start; + LLStrider texcoords0_start; // ownership overlay LLStrider texcoords2_start; LLStrider indices_start; llassert_always(buffer->getVertexStrider(vertices_start)); llassert_always(buffer->getNormalStrider(normals_start)); llassert_always(buffer->getTangentStrider(tangents_start)); + llassert_always(buffer->getTexCoord0Strider(texcoords0_start)); llassert_always(buffer->getTexCoord1Strider(texcoords2_start)); llassert_always(buffer->getIndexStrider(indices_start)); @@ -1037,6 +1059,7 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group) { LLStrider vertices = vertices_start; LLStrider normals = normals_start; + LLStrider texcoords0 = texcoords0_start; LLStrider texcoords2 = texcoords2_start; LLStrider indices = indices_start; @@ -1049,7 +1072,7 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group) facep->setVertexBuffer(buffer); LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject(); - patchp->getTerrainGeometry(vertices, normals, texcoords2, indices); + patchp->getTerrainGeometry(vertices, normals, texcoords0, texcoords2, indices); indices_index += facep->getIndicesCount(); index_offset += facep->getGeomCount(); diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h index af5f05774b..c93a58d2d9 100644 --- a/indra/newview/llvosurfacepatch.h +++ b/indra/newview/llvosurfacepatch.h @@ -57,6 +57,7 @@ public: /*virtual*/ void updateFaceSize(S32 idx); void getTerrainGeometry(LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp); @@ -109,18 +110,21 @@ protected: void updateMainGeometry(LLFace *facep, LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp, U32 &index_offset); void updateNorthGeometry(LLFace *facep, LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp, U32 &index_offset); void updateEastGeometry(LLFace *facep, LLStrider &verticesp, LLStrider &normalsp, + LLStrider &texCoords0p, LLStrider &texCoords1p, LLStrider &indicesp, U32 &index_offset); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index ba8fb0438a..a6409e4a56 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1005,16 +1005,11 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) // animated faces get moved to a smaller partition to reduce // side-effects of their updates (see shrinkWrap in // LLVOVolume::animateTextures). - // FIRE-35018 (Bugsplat) Crash due to spatial group being null - // mDrawable->getSpatialGroup()->dirtyGeom(); - // gPipeline.markRebuild(mDrawable->getSpatialGroup()); - auto spatial_group = mDrawable->getSpatialGroup(); - if (spatial_group) + if (mDrawable->getSpatialGroup()) { - spatial_group->dirtyGeom(); - gPipeline.markRebuild(spatial_group); + mDrawable->getSpatialGroup()->dirtyGeom(); + gPipeline.markRebuild(mDrawable->getSpatialGroup()); } - // } } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 52b5ad65d5..f7ae8defb4 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -1396,4 +1396,10 @@ + + diff --git a/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml b/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml new file mode 100644 index 0000000000..bbad99f932 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml @@ -0,0 +1,31 @@ + + + + diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml index 48235e50ee..aabcc9d364 100644 --- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml @@ -976,8 +976,8 @@ label_width="205" layout="topleft" left="10" - min_val="-100" - max_val="100" + min_val="-10000" + max_val="10000" name="gltfTextureScaleU" top_delta="-115" width="265" /> @@ -989,8 +989,8 @@ label_width="205" layout="topleft" left="10" - min_val="-100" - max_val="100" + min_val="-10000" + max_val="10000" name="gltfTextureScaleV" width="265" />