diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index bd8fc11166..5261b26fd0 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -337,6 +337,7 @@ public: static void addCRLF(string_type& string); static void removeCRLF(string_type& string); + static void removeWindowsCR(string_type& string); static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab ); static void replaceNonstandardASCII( string_type& string, T replacement ); @@ -1327,6 +1328,28 @@ void LLStringUtilBase::removeCRLF(string_type& string) //static template +void LLStringUtilBase::removeWindowsCR(string_type& string) +{ + const T LF = 10; + const T CR = 13; + + size_type cr_count = 0; + size_type len = string.size(); + size_type i; + for( i = 0; i < len - cr_count - 1; i++ ) + { + if( string[i+cr_count] == CR && string[i+cr_count+1] == LF) + { + cr_count++; + } + + string[i] = string[i+cr_count]; + } + string.erase(i, cr_count); +} + +//static +template void LLStringUtilBase::replaceChar( string_type& string, T target, T replacement ) { size_type found_pos = 0; diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index db881954ba..98ea8edd38 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -40,7 +40,8 @@ #include #include -void encode_character(std::ostream& ostr, std::string::value_type val) +// static +void LLURI::encodeCharacter(std::ostream& ostr, std::string::value_type val) { ostr << "%" @@ -95,7 +96,7 @@ std::string LLURI::escape( } else { - encode_character(ostr, c); + encodeCharacter(ostr, c); } } } @@ -106,7 +107,7 @@ std::string LLURI::escape( c = *it; if(allowed.find(c) == std::string::npos) { - encode_character(ostr, c); + encodeCharacter(ostr, c); } else { diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 0b4c9ef56c..d299984dbd 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -121,6 +121,14 @@ public: /** @name Escaping Utilities */ //@{ + /** + * @brief 'Escape' symbol into stream + * + * @param ostr Output stream. + * @param val Symbol to encode. + */ + static void encodeCharacter(std::ostream& ostr, std::string::value_type val); + /** * @brief Escape the string passed except for unreserved * diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index bd57e6c290..4f309037ef 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -339,6 +339,8 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa } face = LLVolumeFace(); + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); point_map.clear(); } } @@ -583,6 +585,8 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector& fac } face = LLVolumeFace(); + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); verts.clear(); indices.clear(); point_map.clear(); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index b1959c7be4..f5b69cf015 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1617,8 +1617,8 @@ void LLTextBase::reflow() segment_set_t::iterator seg_iter = mSegments.begin(); S32 seg_offset = 0; S32 line_start_index = 0; - const S32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin - S32 remaining_pixels = text_available_width; + const F32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin + F32 remaining_pixels = text_available_width; S32 line_count = 0; // find and erase line info structs starting at start_index and going to end of document @@ -1644,14 +1644,15 @@ void LLTextBase::reflow() S32 cur_index = segment->getStart() + seg_offset; // ask segment how many character fit in remaining space - S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, remaining_pixels) : S32_MAX, + S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, ll_round(remaining_pixels)) : S32_MAX, seg_offset, cur_index - line_start_index, S32_MAX, line_count - seg_line_offset); - S32 segment_width, segment_height; - bool force_newline = segment->getDimensions(seg_offset, character_count, segment_width, segment_height); + F32 segment_width; + S32 segment_height; + bool force_newline = segment->getDimensionsF32(seg_offset, character_count, segment_width, segment_height); // grow line height as necessary based on reported height of this segment line_height = llmax(line_height, segment_height); remaining_pixels -= segment_width; @@ -1660,11 +1661,13 @@ void LLTextBase::reflow() S32 last_segment_char_on_line = segment->getStart() + seg_offset; - S32 text_actual_width = text_available_width - remaining_pixels; + // Note: make sure text will fit in width - use ceil, but also make sure + // ceil is used only once per line + S32 text_actual_width = llceil(text_available_width - remaining_pixels); S32 text_left = getLeftOffset(text_actual_width); LLRect line_rect(text_left, cur_top, - text_left + text_actual_width, + text_left + text_actual_width, cur_top - line_height); // if we didn't finish the current segment... @@ -3304,7 +3307,15 @@ boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_bloc LLTextSegment::~LLTextSegment() {} -bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const { width = 0; height = 0; return false;} +bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; } +bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +{ + F32 fwidth = 0; + bool result = getDimensionsF32(first_char, num_chars, fwidth, height); + width = ll_round(fwidth); + return result; +} + S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; } S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const { return 0; } void LLTextSegment::updateLayout(const LLTextBase& editor) {} @@ -3552,7 +3563,7 @@ void LLNormalTextSegment::setToolTip(const std::string& tooltip) mTooltip = tooltip; } -bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { height = 0; width = 0; @@ -3561,7 +3572,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt height = mFontHeight; const LLWString &text = getWText(); // if last character is a newline, then return true, forcing line break - width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); + width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars); } return false; } @@ -3742,7 +3753,7 @@ LLInlineViewSegment::~LLInlineViewSegment() mView->die(); } -bool LLInlineViewSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { if (first_char == 0 && num_chars == 0) { @@ -3829,7 +3840,7 @@ LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLT LLLineBreakTextSegment::~LLLineBreakTextSegment() { } -bool LLLineBreakTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = mFontHeight; @@ -3858,7 +3869,7 @@ LLImageTextSegment::~LLImageTextSegment() static const S32 IMAGE_HPAD = 3; -bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = mStyle->getFont()->getLineHeight(); diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 0b8a80fbda..aca45ca7dd 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -61,8 +61,9 @@ public: mEnd(end) {} virtual ~LLTextSegment(); + bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; - virtual bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; /** @@ -126,7 +127,7 @@ public: LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); virtual ~LLNormalTextSegment(); - /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); @@ -212,7 +213,7 @@ public: LLInlineViewSegment(const Params& p, S32 start, S32 end); ~LLInlineViewSegment(); - /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; /*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); @@ -236,7 +237,7 @@ public: LLLineBreakTextSegment(LLStyleConstSP style,S32 pos); LLLineBreakTextSegment(S32 pos); ~LLLineBreakTextSegment(); - bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); @@ -249,7 +250,7 @@ class LLImageTextSegment : public LLTextSegment public: LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor); ~LLImageTextSegment(); - bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 13a304d2a6..6fe3b8e8cc 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -2835,7 +2835,7 @@ BOOL LLWindowWin32::pasteTextFromClipboard(LLWString &dst) if (utf16str) { dst = utf16str_to_wstring(utf16str); - LLWStringUtil::removeCRLF(dst); + LLWStringUtil::removeWindowsCR(dst); GlobalUnlock(h_data); success = TRUE; } diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b1984ed463..565eb25a0c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3089,6 +3089,17 @@ Value default + ChatAutocompleteGestures + + Comment + Auto-complete gestures in nearby chat + Persist + 1 + Type + Boolean + Value + 1 + ChatBarStealsFocus Comment @@ -12763,6 +12774,17 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 + MaxAttachmentComplexity + + Comment + Attachment's render weight limit + Persist + 1 + Type + F32 + Value + 1.0E6 + ComplexityChangesPopUpDelay Comment @@ -13372,17 +13394,6 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0.02 - ScriptDialogPerObject - - Comment - Controls how script dialogs from the same object are handled (0 = one dialog per object, 1 = one dialog per channel per object, 2 = unconstrained) - Persist - 1 - Type - S32 - Value - 1 - ScriptHelpFollowsCursor Comment @@ -13405,6 +13416,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + ScriptDialogLimitations + + Comment + Limits amount of dialogs per script (0 - per object, 1 - per channel, 2 - per channel for attachments, 3 - per channel for HUDs, 4 -unconstrained for HUDs) + Persist + 1 + Type + U32 + Value + 1 + SecondLifeEnterprise Comment diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index d7a5d32541..eeffe4ae25 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -3307,6 +3307,30 @@ void LLAppearanceMgr::removeAllAttachmentsFromAvatar() removeItemsFromAvatar(ids_to_remove); } +class LLUpdateOnCOFLinkRemove : public LLInventoryCallback +{ +public: + LLUpdateOnCOFLinkRemove(const LLUUID& remove_item_id, LLPointer cb = NULL): + mItemID(remove_item_id), + mCB(cb) + { + } + + /* virtual */ void fire(const LLUUID& item_id) + { + // just removed cof link, "(wear)" suffix depends on presence of link, so update label + gInventory.addChangedMask(LLInventoryObserver::LABEL, mItemID); + if (mCB.notNull()) + { + mCB->fire(item_id); + } + } + +private: + LLUUID mItemID; + LLPointer mCB; +}; + //void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, LLPointer cb) // [SL:KB] - Patch: Appearance-AISFilter | Checked: 2015-05-02 (Catznip-3.7) void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, LLPointer cb, bool immediate_delete) @@ -3330,13 +3354,22 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, LLPointergetUUID(), cb, immediate_delete); // [/RLVa:KB] -// bool immediate_delete = false; // if (item->getType() == LLAssetType::AT_OBJECT) // { -// immediate_delete = true; +// // Immediate delete +// remove_inventory_item(item->getUUID(), cb, true); +// gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); +// } +// else +// { +// // Delayed delete +// // Pointless to update item_id label here since link still exists and first notifyObservers +// // call will restore (wear) suffix, mark for update after deletion +// LLPointer cb_label = new LLUpdateOnCOFLinkRemove(item_id, cb); +// remove_inventory_item(item->getUUID(), cb_label, false); // } - remove_inventory_item(item->getUUID(), cb, immediate_delete); } } } diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 314b859cea..711a87dc99 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -44,7 +44,7 @@ public: mExpanderLabel(more_text) {} - /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { // more label always spans width of text box if (num_chars == 0) diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 57e267057a..899685ea9a 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -495,7 +495,8 @@ void LLFloaterIMNearbyChat::onChatBoxKeystroke() KEY key = gKeyboard->currentKey(); // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 + if (gSavedSettings.getBOOL("ChatAutocompleteGestures") + && length > 1 && raw_text[0] == '/' && key < KEY_SPECIAL) { diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index f1df59ff99..bb2e938f01 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1339,6 +1339,10 @@ LLModelPreview::~LLModelPreview() mPreviewAvatar->markDead(); //*HACK : *TODO : turn this back on when we understand why this crashes //glodShutdown(); + if(mModelLoader) + { + mModelLoader->shutdown(); + } } U32 LLModelPreview::calcResourceCost() diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 213a163bfe..fde5afef75 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3015,86 +3015,94 @@ void LLIMMgr::addMessage( LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg); LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); - skip_message &= !session->isGroupSessionType(); // Do not skip group chats... - if(skip_message) + if (session) { - gIMMgr->leaveSession(new_session_id); - } - // When we get a new IM, and if you are a god, display a bit - // of information about the source. This is to help liaisons - // when answering questions. - if(gAgent.isGodlike()) - { - // *TODO:translate (low priority, god ability) - std::ostringstream bonus_info; - bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " " - << parent_estate_id - << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") - << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : ""); - - // once we have web-services (or something) which returns - // information about a region id, we can print this out - // and even have it link to map-teleport or something. - //<< "*** region_id: " << region_id << std::endl - //<< "*** position: " << position << std::endl; - - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str()); - } - - // Logically it would make more sense to reject the session sooner, in another area of the - // code, but the session has to be established inside the server before it can be left. - if (LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !from_linden) - { - LL_WARNS() << "Leaving IM session from initiating muted resident " << from << LL_ENDL; - if(!gIMMgr->leaveSession(new_session_id)) + skip_message &= !session->isGroupSessionType(); // Do not skip group chats... + if (skip_message) { - LL_INFOS() << "Session " << new_session_id << " does not exist." << LL_ENDL; + gIMMgr->leaveSession(new_session_id); } - return; - } - - // Configurable IM sounds - // //Play sound for new conversations - // if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE)) - - if (dialog != IM_NOTHING_SPECIAL) - { - is_group_chat = gAgent.isInGroup(new_session_id); - } - - // Option to automatically ignore and leave all conference (ad-hoc) chats - static LLCachedControl ignoreAdHocSessions(gSavedSettings, "FSIgnoreAdHocSessions"); - if (dialog != IM_NOTHING_SPECIAL && !is_group_chat && ignoreAdHocSessions && !from_linden) - { - static LLCachedControl dontIgnoreAdHocFromFriends(gSavedSettings, "FSDontIgnoreAdHocFromFriends"); - if (!dontIgnoreAdHocFromFriends || (dontIgnoreAdHocFromFriends && LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL)) + // When we get a new IM, and if you are a god, display a bit + // of information about the source. This is to help liaisons + // when answering questions. + if (gAgent.isGodlike()) { - static LLCachedControl reportIgnoredAdHocSession(gSavedSettings, "FSReportIgnoredAdHocSession"); - LL_INFOS() << "Ignoring conference (ad-hoc) chat from " << new_session_id.asString() << LL_ENDL; + // *TODO:translate (low priority, god ability) + std::ostringstream bonus_info; + bonus_info << LLTrans::getString("***") + " " + LLTrans::getString("IMParentEstate") + ":" + " " + << parent_estate_id + << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") + << ((parent_estate_id == 5) ? "," + LLTrans::getString("IMTeen") : ""); + + // once we have web-services (or something) which returns + // information about a region id, we can print this out + // and even have it link to map-teleport or something. + //<< "*** region_id: " << region_id << std::endl + //<< "*** position: " << position << std::endl; + + LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str()); + } + + // Logically it would make more sense to reject the session sooner, in another area of the + // code, but the session has to be established inside the server before it can be left. + if (LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !from_linden) + { + LL_WARNS() << "Leaving IM session from initiating muted resident " << from << LL_ENDL; if (!gIMMgr->leaveSession(new_session_id)) { - LL_WARNS() << "Ad-hoc session " << new_session_id.asString() << " does not exist." << LL_ENDL; - } - else if (reportIgnoredAdHocSession) - { - report_to_nearby_chat(LLTrans::getString("IgnoredAdHocSession")); + LL_INFOS() << "Session " << new_session_id << " does not exist." << LL_ENDL; } return; } - } - // - if(!do_not_disturb && PlayModeUISndNewIncomingIMSession != 0 && dialog == IM_NOTHING_SPECIAL) - { - make_ui_sound("UISndNewIncomingIMSession"); + // Configurable IM sounds + // //Play sound for new conversations + // if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE)) + + if (dialog != IM_NOTHING_SPECIAL) + { + is_group_chat = gAgent.isInGroup(new_session_id); + } + + // Option to automatically ignore and leave all conference (ad-hoc) chats + static LLCachedControl ignoreAdHocSessions(gSavedSettings, "FSIgnoreAdHocSessions"); + if (dialog != IM_NOTHING_SPECIAL && !is_group_chat && ignoreAdHocSessions && !from_linden) + { + static LLCachedControl dontIgnoreAdHocFromFriends(gSavedSettings, "FSDontIgnoreAdHocFromFriends"); + if (!dontIgnoreAdHocFromFriends || (dontIgnoreAdHocFromFriends && LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL)) + { + static LLCachedControl reportIgnoredAdHocSession(gSavedSettings, "FSReportIgnoredAdHocSession"); + LL_INFOS() << "Ignoring conference (ad-hoc) chat from " << new_session_id.asString() << LL_ENDL; + if (!gIMMgr->leaveSession(new_session_id)) + { + LL_WARNS() << "Ad-hoc session " << new_session_id.asString() << " does not exist." << LL_ENDL; + } + else if (reportIgnoredAdHocSession) + { + report_to_nearby_chat(LLTrans::getString("IgnoredAdHocSession")); + } + return; + } + } + // + + if(!do_not_disturb && PlayModeUISndNewIncomingIMSession != 0 && dialog == IM_NOTHING_SPECIAL) + { + make_ui_sound("UISndNewIncomingIMSession"); + } + else if(!do_not_disturb && PlayModeUISndNewIncomingGroupIMSession != 0 && dialog != IM_NOTHING_SPECIAL && is_group_chat) + { + make_ui_sound("UISndNewIncomingGroupIMSession"); + } + else if(!do_not_disturb && PlayModeUISndNewIncomingConfIMSession != 0 && dialog != IM_NOTHING_SPECIAL && !is_group_chat) + { + make_ui_sound("UISndNewIncomingConfIMSession"); + } } - else if(!do_not_disturb && PlayModeUISndNewIncomingGroupIMSession != 0 && dialog != IM_NOTHING_SPECIAL && is_group_chat) + else { - make_ui_sound("UISndNewIncomingGroupIMSession"); - } - else if(!do_not_disturb && PlayModeUISndNewIncomingConfIMSession != 0 && dialog != IM_NOTHING_SPECIAL && !is_group_chat) - { - make_ui_sound("UISndNewIncomingConfIMSession"); + // Failed to create a session, most likely due to empty name (name cache failed?) + LL_WARNS() << "Failed to create IM session " << fixed_session_name << LL_ENDL; } } else if(!do_not_disturb && PlayModeUISndNewIncomingIMSession == 2 && dialog == IM_NOTHING_SPECIAL) @@ -3503,7 +3511,7 @@ void LLIMMgr::inviteToSession( LLIncomingCallDialog::processCallResponse(1, payload); return; } - else if (LLMuteList::getInstance()->isMuted(caller_id, LLMute::flagAll & ~LLMute::flagVoiceChat)) + else if (LLMuteList::getInstance()->isMuted(caller_id, LLMute::flagAll & ~LLMute::flagVoiceChat) && !voice_invite) { LL_INFOS() << "Rejecting session invite from initiating muted resident " << caller_name << LL_ENDL; return; diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 95066b3df1..efc0d240aa 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -77,13 +77,6 @@ // * Review the download rate throttling. Slow then fast? // Detect bandwidth usage and speed up when it drops? // -// * A lot of calls to notifyObservers(). It looks like -// these could be collapsed by maintaining a 'dirty' -// bit and there appears to be an attempt to do this. -// But it isn't used or is used in a limited fashion. -// Are there semanic issues requiring a call after certain -// updateItem() calls? -// // * An error on a fetch could be due to one item in the batch. // If the batch were broken up, perhaps more of the inventory // would download. (Handwave here, not certain this is an @@ -556,6 +549,12 @@ void LLInventoryModelBackgroundFetch::bulkFetch() { // Process completed background HTTP requests gInventory.handleResponses(false); + // Just processed a bunch of items. + // Note: do we really need notifyObservers() here? + // OnIdle it will be called anyway due to Add flag for processed item. + // It seems like in some cases we are updaiting on fail (no flag), + // but is there anything to update? + gInventory.notifyObservers(); } if ((mFetchCount > max_concurrent_fetches) || @@ -874,7 +873,6 @@ void BGFolderHttpHandler::processData(LLSD & content, LLCore::HttpResponse * res titem->setParent(lost_uuid); titem->updateParentOnServer(FALSE); gInventory.updateItem(titem); - gInventory.notifyObservers(); } } } @@ -947,8 +945,6 @@ void BGFolderHttpHandler::processData(LLSD & content, LLCore::HttpResponse * res { fetcher->setAllFoldersFetched(); } - - gInventory.notifyObservers(); } @@ -991,7 +987,6 @@ void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::Http fetcher->setAllFoldersFetched(); } } - gInventory.notifyObservers(); } @@ -1029,7 +1024,6 @@ void BGFolderHttpHandler::processFailure(const char * const reason, LLCore::Http fetcher->setAllFoldersFetched(); } } - gInventory.notifyObservers(); } diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 5a879f168a..3e11efe037 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -71,7 +71,8 @@ const std::string LL_IM_FROM("from"); const std::string LL_IM_FROM_ID("from_id"); const std::string LL_TRANSCRIPT_FILE_EXTENSION("txt"); -const static std::string IM_SEPARATOR(": "); +const static char IM_SYMBOL_SEPARATOR(':'); +const static std::string IM_SEPARATOR(std::string() + IM_SYMBOL_SEPARATOR + " "); const static std::string NEW_LINE("\n"); const static std::string NEW_LINE_SPACE_PREFIX("\n "); const static std::string TWO_SPACES(" "); @@ -980,7 +981,7 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const } if (im[LL_IM_TIME].isDefined()) -{ + { std::string timestamp = im[LL_IM_TIME].asString(); boost::trim(timestamp); ostr << '[' << timestamp << ']' << TWO_SPACES; @@ -993,9 +994,29 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const { std::string from = im[LL_IM_FROM].asString(); boost::trim(from); - if (from.size()) + + std::size_t found = from.find(IM_SYMBOL_SEPARATOR); + std::size_t len = from.size(); + std::size_t start = 0; + while (found != std::string::npos) { - ostr << from << IM_SEPARATOR; + std::size_t sub_len = found - start; + if (sub_len > 0) + { + ostr << from.substr(start, sub_len); + } + LLURI::encodeCharacter(ostr, IM_SYMBOL_SEPARATOR); + start = found + 1; + found = from.find(IM_SYMBOL_SEPARATOR, start); + } + if (start < len) + { + std::string str_end = from.substr(start, len - start); + ostr << str_end; + } + if (len > 0) + { + ostr << IM_SEPARATOR; } } @@ -1007,7 +1028,7 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX); ostr << im_text; } - } +} bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params) { @@ -1083,7 +1104,7 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params if (!boost::regex_match(stuff, name_and_text, NAME_AND_TEXT)) return false; bool has_name = name_and_text[IDX_NAME].matched; - std::string name = name_and_text[IDX_NAME]; + std::string name = LLURI::unescape(name_and_text[IDX_NAME]); // Handle the case an IM was stored in nearby chat history if (name == "IM:") @@ -1121,7 +1142,7 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params if (divider_pos != std::string::npos && divider_pos < (stuff.length() - NAME_TEXT_DIVIDER.length())) { - im[LL_IM_FROM] = stuff.substr(0, divider_pos); + im[LL_IM_FROM] = LLURI::unescape(stuff.substr(0, divider_pos)); im[LL_IM_TEXT] = stuff.substr(divider_pos + NAME_TEXT_DIVIDER.length()); return true; } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 5857e39b69..ed9045a0e3 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3672,7 +3672,15 @@ void LLMeshRepository::notifyLoadedMeshes() //popup queued error messages from background threads while (!mUploadErrorQ.empty()) { - LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front()); + LLSD substitutions(mUploadErrorQ.front()); + if (substitutions.has("DETAILS")) + { + LLNotificationsUtil::add("MeshUploadErrorDetails", substitutions); + } + else + { + LLNotificationsUtil::add("MeshUploadError", substitutions); + } mUploadErrorQ.pop(); } diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index 0ea3d50c0d..76e3a289a3 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -40,6 +40,7 @@ #include "lltoastnotifypanel.h" #include "lltoastscripttextbox.h" #include "lltrans.h" +#include "llviewerobjectlist.h" #include "llviewerwindow.h" #include "llfloaterimsession.h" @@ -69,6 +70,7 @@ LLUUID notification_id_to_object_id(const LLUUID& notification_id) ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// + LLScriptFloater::LLScriptFloater(const LLSD& key) : LLDockableFloater(NULL, true, key) , mScriptForm(NULL) @@ -425,6 +427,15 @@ void LLScriptFloater::hideToastsIfNeeded() ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +LLScriptFloaterManager::LLScriptFloaterManager() +// script dialogs position +: mNavigationPanelPad(-1), // The height of the favorite and navigation panels might not be known yet + mFavoritesPanelPad(-1) // so don't fill the values in here yet, but remember to do it at first use +// +{ + gSavedSettings.getControl("ScriptDialogLimitations")->getCommitSignal()->connect(boost::bind(&clearScriptNotifications)); +} + void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) { if(notification_id.isNull()) @@ -444,49 +455,78 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) // LLDialog can spawn only one instance, LLLoadURL and LLGiveInventory can spawn unlimited number of instances if(OBJ_SCRIPT == obj_type) { -// // If an Object spawns more-than-one floater, only the newest one is shown. -// // The previous is automatically closed. -// script_notification_map_t::const_iterator it = findUsingObjectId(object_id); -// [SL:KB] - Patch: UI-ScriptDialog | Checked: 2011-01-17 (Catznip-2.4.0h) | Added: Catznip-2.4.0h + static LLCachedControl script_dialog_limitations(gSavedSettings, "ScriptDialogLimitations", 0); script_notification_map_t::const_iterator it = mNotifications.end(); - switch (gSavedSettings.getS32("ScriptDialogPerObject")) + switch (script_dialog_limitations) { - case 0: // One script dialog per object (viewer 2 default) + case SCRIPT_PER_CHANNEL: + { + // If an Object spawns more-than-one floater per channel, only the newest one is shown. + // The previous is automatically closed. + LLNotificationPtr notification = LLNotifications::instance().find(notification_id); + if (notification) + { + it = findUsingObjectIdAndChannel(object_id, notification->getPayload()["chat_channel"].asInteger()); + } + break; + } + case SCRIPT_ATTACHMENT_PER_CHANNEL: + { + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (objectp && objectp->getAttachmentItemID().notNull()) //in user inventory + { + LLNotificationPtr notification = LLNotifications::instance().find(notification_id); + if (notification) + { + it = findUsingObjectIdAndChannel(object_id, notification->getPayload()["chat_channel"].asInteger()); + } + } + else { - // If an Object spawns more-than-one floater, only the newest one is shown. - // The previous is automatically closed. it = findUsingObjectId(object_id); } break; - case 1: // One script dialog per reply channel per object + } + case SCRIPT_HUD_PER_CHANNEL: + { + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (objectp && objectp->isHUDAttachment()) { - // We'll allow an object to have more than one script dialog floater open, but we'll limit it to one per chat channel - // (in practice a lot of objects open a new listen channel for each new dialog but it still reduces chiclets somewhat) - LLNotificationPtr newNotif = LLNotifications::instance().find(notification_id); - if (newNotif) + LLNotificationPtr notification = LLNotifications::instance().find(notification_id); + if (notification) { - S32 nNewChannel = newNotif->getPayload()["chat_channel"].asInteger(); - for (it = mNotifications.begin(); it != mNotifications.end(); ++it) - { - if (it->second == object_id) - { - LLNotificationPtr curNotif = LLNotifications::instance().find(it->first); - if (curNotif) - { - S32 nCurChannel = curNotif->getPayload()["chat_channel"].asInteger(); - if (nNewChannel == nCurChannel) - break; - } - } - } + it = findUsingObjectIdAndChannel(object_id, notification->getPayload()["chat_channel"].asInteger()); } } + else + { + it = findUsingObjectId(object_id); + } break; - case 2: // Unconstrained + } + case SCRIPT_HUD_UNCONSTRAINED: + { + LLViewerObject* objectp = gObjectList.findObject(object_id); + if (objectp && objectp->isHUDAttachment()) + { + // don't remove existing floaters + break; + } + else + { + it = findUsingObjectId(object_id); + } + break; + } + case SCRIPT_PER_OBJECT: default: + { + // If an Object spawns more-than-one floater, only the newest one is shown. + // The previous is automatically closed. + it = findUsingObjectId(object_id); break; + } } -// [/SL:KB] if(it != mNotifications.end()) { @@ -494,7 +534,7 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) if (NULL != chiclet_panelp) { LLIMChiclet * chicletp = chiclet_panelp->findChiclet(it->first); - if(NULL != chicletp) + if (NULL != chicletp) { // Pass the new_message icon state further. set_new_message = chicletp->getShowNewMessagesIcon(); @@ -503,7 +543,7 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) } LLScriptFloater* floater = LLFloaterReg::findTypedInstance("script_floater", it->first); - if(floater) + if (floater) { // Generate chiclet with a "new message" indicator if a docked window was opened but not in focus. See EXT-3142. set_new_message |= !floater->hasFocus(); @@ -709,6 +749,23 @@ LLScriptFloaterManager::script_notification_map_t::const_iterator LLScriptFloate return mNotifications.end(); } +LLScriptFloaterManager::script_notification_map_t::const_iterator LLScriptFloaterManager::findUsingObjectIdAndChannel(const LLUUID& object_id, S32 im_channel) +{ + script_notification_map_t::const_iterator it = mNotifications.begin(); + for (; mNotifications.end() != it; ++it) + { + if (object_id == it->second) + { + LLNotificationPtr notification = LLNotifications::instance().find(it->first); + if (notification && (im_channel == notification->getPayload()["chat_channel"].asInteger())) + { + return it; + } + } + } + return mNotifications.end(); +} + void LLScriptFloaterManager::saveFloaterPosition(const LLUUID& object_id, const FloaterPositionInfo& fpi) { if(object_id.notNull()) @@ -742,15 +799,34 @@ void LLScriptFloaterManager::setFloaterVisible(const LLUUID& notification_id, bo } } -// script dialogs position -// Since we can't initialize the member variables in the class itself, -// we need to do that in the constructor -Zi -LLScriptFloaterManager::LLScriptFloaterManager() -: mNavigationPanelPad(-1), // The height of the favorite and navigation panels might not be known yet - mFavoritesPanelPad(-1) // so don't fill the values in here yet, but remember to do it at first use +//static +void LLScriptFloaterManager::clearScriptNotifications() { + LLScriptFloaterManager* inst = LLScriptFloaterManager::getInstance(); + static const object_type_map TYPE_MAP = initObjectTypeMap(); + + script_notification_map_t::const_iterator ntf_it = inst->mNotifications.begin(); + while (inst->mNotifications.end() != ntf_it) + { + LLUUID notification_id = ntf_it->first; + ntf_it++; // onRemoveNotification() erases notification + LLNotificationPtr notification = LLNotifications::instance().find(notification_id); + if (notification) + { + object_type_map::const_iterator map_it = TYPE_MAP.find(notification->getName()); + if (map_it != TYPE_MAP.end() && map_it->second == OBJ_SCRIPT) + { + if (notification != NULL && !notification->isCancelled()) + { + LLNotificationsUtil::cancel(notification); + } + inst->onRemoveNotification(notification_id); + } + } + } } +// script dialogs position S32 LLScriptFloaterManager::getTopPad() { // initialize if needed diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h index a7ace79c8a..85252c10bb 100644 --- a/indra/newview/llscriptfloater.h +++ b/indra/newview/llscriptfloater.h @@ -41,8 +41,6 @@ class LLScriptFloaterManager : public LLSingleton // *TODO // LLScriptFloaterManager and LLScriptFloater will need some refactoring after we // know how script notifications should look like. - // script dialogs position - //LLSINGLETON_EMPTY_CTOR(LLScriptFloaterManager); LLSINGLETON(LLScriptFloaterManager); public: @@ -55,6 +53,15 @@ public: OBJ_UNKNOWN }EObjectType; + typedef enum e_limitation_type + { + SCRIPT_PER_OBJECT = 0, + SCRIPT_PER_CHANNEL = 1, + SCRIPT_ATTACHMENT_PER_CHANNEL, + SCRIPT_HUD_PER_CHANNEL, + SCRIPT_HUD_UNCONSTRAINED + }ELimitationType; + /** * Handles new notifications. * Saves notification and object ids, removes old notification if needed, creates script chiclet @@ -106,6 +113,11 @@ public: protected: + /** + * Removes all script-dialog notifications + */ + static void clearScriptNotifications(); + typedef std::map object_type_map; static object_type_map initObjectTypeMap(); @@ -114,6 +126,7 @@ protected: typedef std::map script_notification_map_t; script_notification_map_t::const_iterator findUsingObjectId(const LLUUID& object_id); + script_notification_map_t::const_iterator findUsingObjectIdAndChannel(const LLUUID& object_id, S32 im_channel); private: diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 081e604793..41328ac830 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -6379,6 +6379,24 @@ BOOL LLViewerObject::isTempAttachment() const return (mID.notNull() && (mID == mAttachmentItemID)); } +BOOL LLViewerObject::isHiglightedOrBeacon() const +{ + if (LLFloaterReg::instanceVisible("beacons") && (gPipeline.getRenderBeacons(NULL) || gPipeline.getRenderHighlights(NULL))) + { + BOOL has_media = (getMediaType() == LLViewerObject::MEDIA_SET); + BOOL is_scripted = !isAvatar() && !getParent() && flagScripted(); + BOOL is_physical = !isAvatar() && flagUsePhysics(); + + return (isParticleSource() && gPipeline.getRenderParticleBeacons(NULL)) + || (isAudioSource() && gPipeline.getRenderSoundBeacons(NULL)) + || (has_media && gPipeline.getRenderMOAPBeacons(NULL)) + || (is_scripted && gPipeline.getRenderScriptedBeacons(NULL)) + || (is_scripted && flagHandleTouch() && gPipeline.getRenderScriptedTouchBeacons(NULL)) + || (is_physical && gPipeline.getRenderPhysicalBeacons(NULL)); + } + return FALSE; +} + const LLUUID &LLViewerObject::getAttachmentItemID() const { diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 7e47764a96..ba14df6bf8 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -178,6 +178,8 @@ public: virtual BOOL isHUDAttachment() const { return FALSE; } virtual BOOL isTempAttachment() const; + virtual BOOL isHiglightedOrBeacon() const; + virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() @@ -395,7 +397,7 @@ public: // Create if necessary LLAudioSource *getAudioSource(const LLUUID& owner_id); - bool isAudioSource() {return mAudioSourcep != NULL;} + BOOL isAudioSource() const {return mAudioSourcep != NULL;} U8 getMediaType() const; void setMediaType(U8 media_type); diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 6a4a73f7d7..3d5e02c4b2 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -214,7 +214,7 @@ public: mToolTip = inv_item->getName() + '\n' + inv_item->getDescription(); } - /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { if (num_chars == 0) { @@ -223,7 +223,7 @@ public: } else { - width = EMBEDDED_ITEM_LABEL_PADDING + mImage->getWidth() + mStyle->getFont()->getWidth(mLabel.c_str()); + width = EMBEDDED_ITEM_LABEL_PADDING + mImage->getWidth() + mStyle->getFont()->getWidthF32(mLabel.c_str()); height = llmax(mImage->getHeight(), mStyle->getFont()->getLineHeight()); } return false; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 212f1254c6..1feaa08a2c 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -134,7 +134,7 @@ const F32 MAX_HOVER_Z = 2.0f; const F32 MIN_HOVER_Z = -2.0f; const F32 MIN_ATTACHMENT_COMPLEXITY = 0.f; -const F32 MAX_ATTACHMENT_COMPLEXITY = 1.0e6f; +const F32 DEFAULT_MAX_ATTACHMENT_COMPLEXITY = 1.0e6f; using namespace LLAvatarAppearanceDefines; @@ -9918,6 +9918,9 @@ void LLVOAvatar::calculateUpdateRenderComplexity() * the official viewer for consideration. *****************************************************************/ static const U32 COMPLEXITY_BODY_PART_COST = 200; + static LLCachedControl max_complexity_setting(gSavedSettings,"MaxAttachmentComplexity"); + F32 max_attachment_complexity = max_complexity_setting; + max_attachment_complexity = llmax(max_attachment_complexity, DEFAULT_MAX_ATTACHMENT_COMPLEXITY); // Diagnostic list of all textures on our avatar // Disable useless diagnostics @@ -10008,7 +10011,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() << " children: " << attachment_children_cost << LL_ENDL; // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI - cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, MAX_ATTACHMENT_COMPLEXITY); + cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity); } } } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 4bb7def6f2..a7f651d786 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -4061,7 +4061,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& start_face = face; end_face = face+1; } - + pick_transparent |= isHiglightedOrBeacon(); bool special_cursor = specialHoverCursor(); for (S32 i = start_face; i < end_face; ++i) { diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index d132e7f7ea..7d60adcf51 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -908,6 +908,108 @@ Please invite members within 48 hours. yestext="Create group for L$[COST]"/> + +This group is not accessible to you. + group_id + success + + + + +Error processing your group membership request. + group_id + success + + + + +Unable to join group: [reason] + group_id + success + reason + + + + +Sorry, trial users can't join groups. + group_id + success + + + + +You cannot join '[group_name]': +You are already a member of [group_count] groups, the maximum number allowed is [max_groups] + success + group_id + group_name + group_count + max_groups + + + + +You cannot join '[group_name]': +The group no longer has open enrollment. + group_id + success + + + + +You have been added to the group + group_id + success + + + + +Unable to transfer the required L$ [membership_fee] membership fee. + group_id + success + + + [LABEL] failed to upload: [MESSAGE] [IDENTIFIER] [DETAILS] See Firestorm.log for details + + + [LABEL] failed to upload: [MESSAGE] +[IDENTIFIER] + +See Firestorm.log for details + + +