From 5608c3998df99c9ea075c58f0f45fb23617ec2ed Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 8 Oct 2024 22:10:50 +0300 Subject: viewer#2270 The "More" button does not close the "Choose emoji" floater --- indra/newview/llfloaterimsessiontab.cpp | 42 +++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'indra/newview/llfloaterimsessiontab.cpp') diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 655674357f..50e765c236 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -39,6 +39,7 @@ #include "llchicletbar.h" #include "lldraghandle.h" #include "llemojidictionary.h" +#include "llemojihelper.h" #include "llfloaterreg.h" #include "llfloateremojipicker.h" #include "llfloaterimsession.h" @@ -300,6 +301,8 @@ bool LLFloaterIMSessionTab::postBuild() mEmojiPickerShowBtn = getChild("emoji_picker_show_btn"); mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); }); + mEmojiPickerShowBtn->setMouseDownCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnDown(); }); + mEmojiCloseConn = LLEmojiHelper::instance().setCloseCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerClosed(); }); mGearBtn = getChild("gear_btn"); mAddBtn = getChild("add_btn"); @@ -532,8 +535,43 @@ void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked() void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked() { - mInputEditor->setFocus(true); - mInputEditor->showEmojiHelper(); + if (!mEmojiPickerShowBtn->getToggleState()) + { + mInputEditor->hideEmojiHelper(); + mInputEditor->setFocus(true); + mInputEditor->showEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(true); // in case hideEmojiHelper closed a visible instance + } + else + { + mInputEditor->hideEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(false); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerShowBtnDown() +{ + if (mEmojiHelperLastCallbackFrame == LLFrameTimer::getFrameCount()) + { + // Helper gets closed by focus lost event on Down before before onEmojiPickerShowBtnDown + // triggers. + // If this condition is true, user pressed button and it was 'toggled' during press, + // restore 'toggled' state so that button will not reopen helper. + mEmojiPickerShowBtn->setToggleState(true); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerClosed() +{ + if (mEmojiPickerShowBtn->getToggleState()) + { + mEmojiPickerShowBtn->setToggleState(false); + // Helper gets closed by focus lost event on Down before onEmojiPickerShowBtnDown + // triggers. If mEmojiHelperLastCallbackFrame is set and matches Down, means close + // was triggered by user's press. + // A bit hacky, but I can't think of a better way to handle this without rewriting helper. + mEmojiHelperLastCallbackFrame = LLFrameTimer::getFrameCount(); + } } void LLFloaterIMSessionTab::initEmojiRecentPanel() -- cgit v1.3 From 3e46d707a243e91046b3ab0af8f844d7f40f77b4 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 18 Apr 2025 17:48:02 +0300 Subject: #3758 initial chat mention support --- indra/llui/CMakeLists.txt | 2 + indra/llui/llchatentry.cpp | 19 +++ indra/llui/llchatentry.h | 2 + indra/llui/llchatmentionhelper.cpp | 151 +++++++++++++++++ indra/llui/llchatmentionhelper.h | 66 ++++++++ indra/llui/llflatlistview.cpp | 6 +- indra/llui/llflatlistview.h | 4 + indra/llui/lltextbase.cpp | 4 +- indra/llui/lltextbase.h | 2 +- indra/llui/lltexteditor.cpp | 50 +++++- indra/llui/lltexteditor.h | 4 + indra/llui/llurlentry.cpp | 4 +- indra/newview/CMakeLists.txt | 2 + indra/newview/llavatarlist.cpp | 18 +- indra/newview/llavatarlist.h | 7 +- indra/newview/llavatarlistitem.cpp | 17 +- indra/newview/llavatarlistitem.h | 3 +- indra/newview/llfloaterchatmentionpicker.cpp | 183 +++++++++++++++++++++ indra/newview/llfloaterchatmentionpicker.h | 58 +++++++ indra/newview/llfloaterimnearbychat.cpp | 2 +- indra/newview/llfloaterimsession.cpp | 2 +- indra/newview/llfloaterimsessiontab.cpp | 2 + indra/newview/llviewerfloaterreg.cpp | 2 + .../default/xui/en/floater_chat_mention_picker.xml | 31 ++++ 24 files changed, 617 insertions(+), 24 deletions(-) create mode 100644 indra/llui/llchatmentionhelper.cpp create mode 100644 indra/llui/llchatmentionhelper.h create mode 100644 indra/newview/llfloaterchatmentionpicker.cpp create mode 100644 indra/newview/llfloaterchatmentionpicker.h create mode 100644 indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml (limited to 'indra/newview/llfloaterimsessiontab.cpp') diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index a0314cb5f2..908e94b24c 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -18,6 +18,7 @@ set(llui_SOURCE_FILES llbadgeowner.cpp llbutton.cpp llchatentry.cpp + llchatmentionhelper.cpp llcheckboxctrl.cpp llclipboard.cpp llcombobox.cpp @@ -130,6 +131,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 da5afd0386..55e4beafb6 100644 --- a/indra/llui/llchatentry.cpp +++ b/indra/llui/llchatentry.cpp @@ -51,6 +51,7 @@ LLChatEntry::LLChatEntry(const Params& p) mCurrentHistoryLine = mLineHistory.begin(); mAutoIndent = false; + mShowChatMentionPicker = true; keepSelectionOnReturn(true); } @@ -249,3 +250,21 @@ void LLChatEntry::enableSingleLineMode(bool single_line_mode) mPrevLinesCount = -1; setWordWrap(!single_line_mode); } + +LLWString LLChatEntry::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/llchatentry.h b/indra/llui/llchatentry.h index 5621ede1e7..bb5eb8024d 100644 --- a/indra/llui/llchatentry.h +++ b/indra/llui/llchatentry.h @@ -68,6 +68,8 @@ public: void enableSingleLineMode(bool single_line_mode); boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); + LLWString getConvertedText() const; + private: /** diff --git a/indra/llui/llchatmentionhelper.cpp b/indra/llui/llchatmentionhelper.cpp new file mode 100644 index 0000000000..98d846b947 --- /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) +{ + 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..4da8c8264e --- /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); + 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 53f39766c6..25fe9f2556 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -459,6 +459,7 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) , mNoItemsCommentTextbox(NULL) , mIsConsecutiveSelection(false) , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape) + , mFocusOnItemClicked(true) { mBorderThickness = getBorderWidth(); @@ -610,7 +611,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 6d75e9f282..2a06ded5cb 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -299,6 +299,8 @@ public: virtual S32 notify(const LLSD& info) ; + void setFocusOnItemClicked(bool b) { mFocusOnItemClicked = b; } + virtual ~LLFlatListView(); protected: @@ -423,6 +425,8 @@ private: bool mKeepSelectionVisibleOnReshape; + bool mFocusOnItemClicked; + /** All pairs of the list */ pairs_list_t mItemPairs; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index cb682a3625..5f62763683 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -2322,14 +2322,14 @@ 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; diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index fa8d22c819..a2895e6bd6 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -676,7 +676,7 @@ 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 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); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index fe4cce29ab..bd726c3d49 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" @@ -270,6 +271,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) @@ -714,6 +716,18 @@ 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); + setCursorPos(mention_start_pos); + + appendTextImpl(name_url, LLStyle::Params(), true); + } +} + bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { bool handled = false; @@ -1103,6 +1117,7 @@ void LLTextEditor::removeCharOrTab() } tryToShowEmojiHelper(); + tryToShowMentionHelper(); } else { @@ -1128,6 +1143,7 @@ void LLTextEditor::removeChar() setCursorPos(mCursorPos - 1); removeChar(mCursorPos); tryToShowEmojiHelper(); + tryToShowMentionHelper(); } else { @@ -1189,6 +1205,7 @@ void LLTextEditor::addChar(llwchar wc) setCursorPos(mCursorPos + addChar( mCursorPos, wc )); tryToShowEmojiHelper(); + tryToShowMentionHelper(); if (!mReadOnly && mAutoreplaceCallback != NULL) { @@ -1247,6 +1264,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() ) @@ -1884,9 +1926,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 && diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index b2b14b01e2..e38908734f 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -95,6 +95,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); @@ -258,6 +260,7 @@ protected: S32 remove(S32 pos, S32 length, bool group_with_next_op); void tryToShowEmojiHelper(); + void tryToShowMentionHelper(); void focusLostHelper(); void updateAllowingLanguageInput(); bool hasPreeditString() const; @@ -295,6 +298,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 ce5ff0ff75..9657dc9527 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -580,7 +580,7 @@ LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() // LLUrlEntryAgent::LLUrlEntryAgent() { - mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/(mention|(?!mention)\\w+)", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_agent.xml"; mIcon = "Generic_Person"; @@ -784,7 +784,7 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url) { // *NOTE: Could look up a badge here by calling getIDStringFromUrl() // and looking up the badge for the agent. - return mIcon; + return LLStringUtil::endsWith(url, "/mention") ? std::string() : mIcon; } // diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fff1597fd9..98151e2f4d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -201,6 +201,7 @@ set(viewer_SOURCE_FILES llfloatercamera.cpp llfloatercamerapresets.cpp llfloaterchangeitemthumbnail.cpp + llfloaterchatmentionpicker.cpp llfloaterchatvoicevolume.cpp llfloaterclassified.cpp llfloatercolorpicker.cpp @@ -870,6 +871,7 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamerapresets.h llfloaterchangeitemthumbnail.h + llfloaterchatmentionpicker.h llfloatercamera.h llfloaterchatvoicevolume.h llfloaterclassified.h diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 8f858fe4e1..f206474e71 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -141,6 +141,7 @@ LLAvatarList::LLAvatarList(const Params& p) , mShowSpeakingIndicator(p.show_speaking_indicator) , mShowPermissions(p.show_permissions_granted) , mShowCompleteName(false) +, mForceCompleteName(false) { setCommitOnSelectionChange(true); @@ -177,7 +178,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(); } // virtual @@ -364,7 +365,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; @@ -404,6 +405,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) { @@ -418,7 +424,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); // This sets the name as a side effect item->setAvatarId(id, mSessionID, mIgnoreOnlineStatus); item->setOnline(mIgnoreOnlineStatus ? true : is_online); @@ -432,6 +438,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is 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); } @@ -550,6 +557,11 @@ void LLAvatarList::onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) mItemDoubleClickSignal(ctrl, x, y, mask); } +void LLAvatarList::onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + mItemClickedSignal(ctrl, x, y, mask); +} + bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const { const LLAvatarListItem* avatar_item1 = dynamic_cast(item1); diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index af5bfefcde..f99da93a3d 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -96,11 +96,13 @@ public: 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(); @@ -113,6 +115,7 @@ protected: void updateLastInteractionTimes(); void rebuildNames(); void onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); + void onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); void updateAvatarNames(); private: @@ -127,6 +130,7 @@ private: bool mShowSpeakingIndicator; bool mShowPermissions; bool mShowCompleteName; + bool mForceCompleteName; LLTimer* mLITUpdateTimer; // last interaction time update timer std::string mIconParamName; @@ -138,6 +142,7 @@ private: commit_signal_t mRefreshCompleteSignal; mouse_signal_t mItemDoubleClickSignal; + mouse_signal_t mItemClickedSignal; }; /** Abstract comparator for avatar items */ diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 880910d18e..6ef45ed160 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -78,6 +78,7 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) mShowProfileBtn(true), mShowPermissions(false), mShowCompleteName(false), + mForceCompleteName(false), mHovered(false), mAvatarNameCacheConnection(), mGreyOutUsername("") @@ -324,13 +325,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) @@ -417,8 +416,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); diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 2e4c597d30..2ec7a41055 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -106,7 +106,7 @@ public: void setShowPermissions(bool show) { mShowPermissions = 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;}; const LLUUID& getAvatarId() const; std::string getAvatarName() const; @@ -220,6 +220,7 @@ private: bool mHovered; bool mShowCompleteName; + bool mForceCompleteName; std::string mGreyOutUsername; void fetchAvatarName(); diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp new file mode 100644 index 0000000000..dda2cd83f6 --- /dev/null +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -0,0 +1,183 @@ +/** + * @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; +} + +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: + 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/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index db6f9ac22a..b649514bff 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -586,7 +586,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 185274981b..84a9fad708 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -251,7 +251,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 50e765c236..96aac8c1e6 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -35,6 +35,7 @@ #include "llavatariconctrl.h" #include "llchatentry.h" #include "llchathistory.h" +#include "llfloaterchatmentionpicker.h" #include "llchiclet.h" #include "llchicletbar.h" #include "lldraghandle.h" @@ -485,6 +486,7 @@ void LLFloaterIMSessionTab::onFocusReceived() LLIMModel::instance().sendNoUnreadMessages(mSessionID); } + LLFloaterChatMentionPicker::updateSessionID(mSessionID); super::onFocusReceived(); } diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 95c2a77ba8..4d9c2f3281 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" @@ -353,6 +354,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); + 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/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 @@ + + + + -- cgit v1.3 From 9180a110bd5b0e41f182c32017b5dccd2eb265be Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 5 May 2025 21:13:11 +0300 Subject: #4028 Crash at onEmojiPickerClosed --- indra/newview/llfloaterimsessiontab.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/newview/llfloaterimsessiontab.cpp') diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 96aac8c1e6..7c3d2b511b 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -106,6 +106,7 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab() { delete mRefreshTimer; LLIMMgr::instance().removeSessionObserver(this); + mEmojiCloseConn.disconnect(); LLFloaterIMContainer* im_container = LLFloaterIMContainer::findInstance(); if (im_container) -- cgit v1.3 From 4bc20a8e536fddf5e48e5d963a39e3dae0fb733f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 26 Jun 2025 23:31:21 +0300 Subject: #4184 clearAndDeparentModels crash To resolve dual ownership switched children to LLPointer --- indra/llui/llfolderviewmodel.h | 67 ++++++++++++++-------------- indra/newview/llconversationmodel.cpp | 20 ++++----- indra/newview/llfloaterchatmentionpicker.cpp | 2 +- indra/newview/llfloaterimcontainer.cpp | 6 +-- indra/newview/llfloaterimsessiontab.cpp | 45 +------------------ indra/newview/llinventoryfunctions.cpp | 10 +++-- 6 files changed, 54 insertions(+), 96 deletions(-) (limited to 'indra/newview/llfloaterimsessiontab.cpp') diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index ba9d0b8de3..2865b789b9 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -224,6 +224,7 @@ public: virtual S32 getSortVersion() = 0; virtual void setSortVersion(S32 version) = 0; virtual void setParent(LLFolderViewModelItem* parent) = 0; + virtual const LLFolderViewModelItem* getParent() = 0; virtual bool hasParent() = 0; protected: @@ -254,14 +255,14 @@ public: mChildren.clear(); } - void requestSort() { mSortVersion = -1; } - S32 getSortVersion() { return mSortVersion; } - void setSortVersion(S32 version) { mSortVersion = version;} + void requestSort() override { mSortVersion = -1; } + S32 getSortVersion() override { return mSortVersion; } + void setSortVersion(S32 version) override { mSortVersion = version;} - S32 getLastFilterGeneration() const { return mLastFilterGeneration; } + S32 getLastFilterGeneration() const override { return mLastFilterGeneration; } S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; } - S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; } - void dirtyFilter() + S32 getMarkedDirtyGeneration() const override { return mMarkedDirtyGeneration; } + void dirtyFilter() override { if(mMarkedDirtyGeneration < 0) { @@ -276,7 +277,7 @@ public: mParent->dirtyFilter(); } } - void dirtyDescendantsFilter() + void dirtyDescendantsFilter() override { mMostFilteredDescendantGeneration = -1; if (mParent) @@ -284,13 +285,13 @@ public: mParent->dirtyDescendantsFilter(); } } - bool hasFilterStringMatch(); - std::string::size_type getFilterStringOffset(); - std::string::size_type getFilterStringSize(); + bool hasFilterStringMatch() override; + std::string::size_type getFilterStringOffset() override; + std::string::size_type getFilterStringSize() override; - typedef std::list child_list_t; + typedef std::list > child_list_t; - virtual void addChild(LLFolderViewModelItem* child) + virtual void addChild(LLFolderViewModelItem* child) override { mChildren.push_back(child); child->setParent(this); @@ -298,15 +299,15 @@ public: requestSort(); } - virtual void removeChild(LLFolderViewModelItem* child) + virtual void removeChild(LLFolderViewModelItem* child) override final { - mChildren.remove(child); child->setParent(NULL); + mChildren.remove(child); dirtyDescendantsFilter(); dirtyFilter(); } - virtual void clearChildren() + virtual void clearChildren() override { // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); }); @@ -319,7 +320,7 @@ public: child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); } child_list_t::size_type getChildrenCount() const { return mChildren.size(); } - void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) + void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) override { mPassedFilter = passed; mLastFilterGeneration = filter_generation; @@ -328,20 +329,20 @@ public: mMarkedDirtyGeneration = -1; } - void setPassedFolderFilter(bool passed, S32 filter_generation) + void setPassedFolderFilter(bool passed, S32 filter_generation) override { mPassedFolderFilter = passed; mLastFolderFilterGeneration = filter_generation; } - virtual bool potentiallyVisible() + virtual bool potentiallyVisible() override { return passedFilter() // we've passed the filter || (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet || descendantsPassedFilter(); } - virtual bool passedFilter(S32 filter_generation = -1) + virtual bool passedFilter(S32 filter_generation = -1) override { if (filter_generation < 0) { @@ -352,7 +353,7 @@ public: return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation)); } - virtual bool descendantsPassedFilter(S32 filter_generation = -1) + virtual bool descendantsPassedFilter(S32 filter_generation = -1) override { if (filter_generation < 0) { @@ -361,10 +362,10 @@ public: return mMostFilteredDescendantGeneration >= filter_generation; } - protected: - virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; } - virtual bool hasParent() { return mParent != NULL; } + virtual void setParent(LLFolderViewModelItem* parent) override final { mParent = parent; } + virtual const LLFolderViewModelItem* getParent() override { return mParent; }; + virtual bool hasParent() override { return mParent != NULL; } S32 mSortVersion; bool mPassedFilter; @@ -381,7 +382,7 @@ protected: LLFolderViewModelItem* mParent; LLFolderViewModelInterface& mRootViewModel; - void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;} + void setFolderViewItem(LLFolderViewItem* folder_view_item) override { mFolderViewItem = folder_view_item;} LLFolderViewItem* mFolderViewItem; }; @@ -395,15 +396,15 @@ public: mFolderView(NULL) {} - virtual void requestSortAll() + virtual void requestSortAll() override { // sort everything mTargetSortVersion++; } - virtual std::string getStatusText(bool is_empty_folder = false); - virtual void filter(); + virtual std::string getStatusText(bool is_empty_folder = false) override; + virtual void filter() override; - void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} + void setFolderView(LLFolderView* folder_view) override { mFolderView = folder_view;} protected: bool needsSort(class LLFolderViewModelItem* item); @@ -433,14 +434,14 @@ public: virtual const SortType& getSorter() const { return *mSorter; } virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); } - virtual FilterType& getFilter() { return *mFilter; } - virtual const FilterType& getFilter() const { return *mFilter; } + virtual FilterType& getFilter() override { return *mFilter; } + virtual const FilterType& getFilter() const override { return *mFilter; } virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); } // By default, we assume the content is available. If a network fetch mechanism is implemented for the model, // this method needs to be overloaded and return the relevant fetch status. - virtual bool contentsReady() { return true; } - virtual bool isFolderComplete(LLFolderViewFolder* folder) { return true; } + virtual bool contentsReady() override { return true; } + virtual bool isFolderComplete(LLFolderViewFolder* folder) override { return true; } struct ViewModelCompare { @@ -461,7 +462,7 @@ public: const SortType& mSorter; }; - void sort(LLFolderViewFolder* folder) + void sort(LLFolderViewFolder* folder) override { if (needsSort(folder->getViewModelItem())) { diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 5004055666..bb1daf4ec1 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -300,7 +300,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic for (auto itemp : mChildren) { - LLConversationItem* current_participant = dynamic_cast(itemp); + LLConversationItem* current_participant = dynamic_cast(itemp.get()); // Add the avatar uuid to the list (except if it's the own agent uuid) if (current_participant->getUUID() != gAgentID) { @@ -329,6 +329,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant) { + LLPointer holder(participant); removeChild(participant); mNeedsRefresh = true; updateName(participant); @@ -360,15 +361,10 @@ void LLConversationItemSession::clearAndDeparentModels() for (child_list_t::iterator it = mChildren.begin(); it != mChildren.end();) { LLFolderViewModelItem* child = *it; - if (child->getNumRefs() == 0) + // Note that model might still be assigned to some view/widget + // and have a different parent + if (child->getParent() == this) { - // LLConversationItemParticipant can be created but not assigned to any view, - // it was waiting for an "add_participant" event to be processed - delete child; - } - else - { - // Model is still assigned to some view/widget child->setParent(NULL); } it = mChildren.erase(it); @@ -383,7 +379,7 @@ LLConversationItemParticipant* LLConversationItemSession::findParticipant(const child_list_t::iterator iter; for (iter = mChildren.begin(); iter != mChildren.end(); iter++) { - participant = dynamic_cast(*iter); + participant = dynamic_cast((*iter).get()); if (participant && participant->hasSameValue(participant_id)) { break; @@ -493,7 +489,7 @@ const bool LLConversationItemSession::getTime(F64& time) const child_list_t::const_iterator iter; for (iter = mChildren.begin(); iter != mChildren.end(); iter++) { - participant = dynamic_cast(*iter); + participant = dynamic_cast((*iter).get()); F64 participant_time; if (participant && participant->getTime(participant_time)) { @@ -517,7 +513,7 @@ void LLConversationItemSession::dumpDebugData(bool dump_children) { for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); iter++) { - LLConversationItemParticipant* participant = dynamic_cast(*iter); + LLConversationItemParticipant* participant = dynamic_cast((*iter).get()); if (participant) { participant->dumpDebugData(); diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp index 1cfed122a9..a3eb286375 100644 --- a/indra/newview/llfloaterchatmentionpicker.cpp +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -88,7 +88,7 @@ uuid_vec_t LLFloaterChatMentionPicker::getParticipantIds() 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); + LLConversationItem* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { avatar_ids.push_back(participant_model->getUUID()); diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 72d4d30dcf..4312c058a1 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -460,7 +460,7 @@ void LLFloaterIMContainer::processParticipantsStyleUpdate() LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); while (current_participant_model != end_participant_model) { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + LLConversationItemParticipant* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { // Get the avatar name for this participant id from the cache and update the model @@ -511,7 +511,7 @@ void LLFloaterIMContainer::idleUpdate() bool can_ban = haveAbilityToBan(); while (current_participant_model != end_participant_model) { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + LLConversationItemParticipant* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { participant_model->setModeratorOptionsVisible(is_moderator); @@ -1874,7 +1874,7 @@ LLConversationItem* LLFloaterIMContainer::addConversationListItem(const LLUUID& 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); + LLConversationItem* participant_model = dynamic_cast((*current_participant_model).get()); LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); participant_view->addToFolder(widget); current_participant_model++; diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 7c3d2b511b..e03422780a 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -107,26 +107,6 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab() delete mRefreshTimer; LLIMMgr::instance().removeSessionObserver(this); mEmojiCloseConn.disconnect(); - - LLFloaterIMContainer* im_container = LLFloaterIMContainer::findInstance(); - if (im_container) - { - LLParticipantList* session = dynamic_cast(im_container->getSessionModel(mSessionID)); - if (session) - { - for (const conversations_widgets_map::value_type& widget_pair : mConversationsWidgets) - { - LLFolderViewItem* widget = widget_pair.second; - LLFolderViewModelItem* item_vmi = widget->getViewModelItem(); - if (item_vmi && item_vmi->getNumRefs() == 1) - { - // This is the last pointer, remove participant from session - // before participant gets deleted on destroyView. - session->removeChild(item_vmi); - } - } - } - } } // static @@ -730,7 +710,7 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant() 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); + LLConversationItem* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { addConversationViewParticipant(participant_model); @@ -774,27 +754,6 @@ void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& part LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); if (widget) { - LLFolderViewModelItem* item_vmi = widget->getViewModelItem(); - if (item_vmi && item_vmi->getNumRefs() == 1) - { - // This is the last pointer, remove participant from session - // before participant gets deleted on destroyView. - // - // Floater (widget) and participant's view can simultaneously - // co-own the model, in which case view is responsible for - // the deletion and floater is free to clear and recreate - // the list, yet there are cases where only widget owns - // the pointer so it should do the cleanup. - // See "add_participant". - // - // Todo: If it keeps causing issues turn participants - // into LLPointers in the session - LLParticipantList* session = getParticipantList(); - if (session) - { - session->removeChild(item_vmi); - } - } widget->destroyView(); } mConversationsWidgets.erase(participant_id); @@ -860,7 +819,7 @@ void LLFloaterIMSessionTab::refreshConversation() LLIMSpeakerMgr *speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); while (current_participant_model != end_participant_model) { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + LLConversationItemParticipant* participant_model = dynamic_cast((*current_participant_model).get()); if (speaker_mgr && participant_model) { LLSpeaker *participant_speaker = speaker_mgr->findSpeaker(participant_model->getUUID()); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index b577e302a8..0365b06edb 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -3998,15 +3998,17 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) for (; set_iter != selected_items.end(); ++set_iter) { viewModel = dynamic_cast((*set_iter)->getViewModelItem()); - if (!viewModel || !viewModel->getInventoryObject()) continue; - if (gInventory.isObjectDescendentOf(viewModel->getInventoryObject()->getParentUUID(), marketplacelistings_id)) + if (!viewModel) continue; + LLInventoryObject* inv_obj = viewModel->getInventoryObject(); + if (!inv_obj) continue; + if (gInventory.isObjectDescendentOf(inv_obj->getParentUUID(), marketplacelistings_id)) { - const LLUUID &parent_id = viewModel->getInventoryObject()->getParentUUID(); + const LLUUID &parent_id = inv_obj->getParentUUID(); if (parent_id != marketplacelistings_id) { sMarketplaceFolders.push_back(parent_id); } - const LLUUID &curr_id = viewModel->getInventoryObject()->getUUID(); + const LLUUID &curr_id = inv_obj->getUUID(); if (curr_id != marketplacelistings_id) { sMarketplaceFolders.push_back(curr_id); -- cgit v1.3 From 003092955486dfb0882d49d6b40586431df6368b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 10 Oct 2024 23:42:05 +0300 Subject: viewer#2172 AM/PM selector --- indra/newview/app_settings/settings.xml | 4 +- indra/newview/llchathistory.cpp | 1 + indra/newview/llconversationlog.cpp | 20 ++++++--- indra/newview/llfloaterimsessiontab.cpp | 15 ++++++- indra/newview/llfloaterinspect.cpp | 3 +- indra/newview/llfloaterland.cpp | 3 +- indra/newview/llfloaterpreference.cpp | 11 +++++ indra/newview/llfloaterpreference.h | 2 + indra/newview/lllogchat.cpp | 48 +++++++++++----------- indra/newview/llnotificationlistitem.cpp | 21 ++++++++-- indra/newview/llpanellandmarkinfo.cpp | 4 +- indra/newview/llpanelteleporthistory.cpp | 15 ++++++- indra/newview/llsidepaneliteminfo.cpp | 3 +- indra/newview/lltoastgroupnotifypanel.cpp | 19 +++++++-- indra/newview/llworldmap.cpp | 18 ++++++-- .../skins/default/xui/da/sidepanel_item_info.xml | 3 ++ .../skins/default/xui/de/sidepanel_item_info.xml | 3 ++ .../skins/default/xui/en/floater_about_land.xml | 3 ++ .../skins/default/xui/en/floater_inspect.xml | 4 ++ .../newview/skins/default/xui/en/notifications.xml | 2 +- .../skins/default/xui/en/panel_landmark_info.xml | 4 ++ .../skins/default/xui/en/panel_place_profile.xml | 4 ++ .../default/xui/en/panel_preferences_general.xml | 31 ++++++++++++++ .../skins/default/xui/en/sidepanel_item_info.xml | 4 ++ indra/newview/tests/llworldmap_test.cpp | 6 +++ 25 files changed, 201 insertions(+), 50 deletions(-) (limited to 'indra/newview/llfloaterimsessiontab.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d64f82d303..aa6a28f137 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13036,9 +13036,9 @@ Use24HourClock Comment - 12 vs 24. At the moment only for region restart schedule floater + 12 vs 24. At the moment coverage is partial Persist - 0 + 1 Type Boolean Value diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 1988e2072b..2facc43811 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -441,6 +441,7 @@ public: time_t current_time = time_corrected(); time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime); + // Report abuse shouldn't use AM/PM, use 24-hour time time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" + LLTrans::getString("TimeYear") + "] [" diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index cea68c1779..65a068e08d 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -118,11 +118,21 @@ const std::string LLConversation::createTimestamp(const U64Seconds& utc_time) LLSD substitution; substitution["datetime"] = (S32)utc_time.value(); - timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" - +LLTrans::getString ("TimeDay")+"]/[" - +LLTrans::getString ("TimeYear")+"] [" - +LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] ["; + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index e03422780a..65c13797ac 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -618,8 +618,19 @@ void LLFloaterIMSessionTab::deleteAllChildren() std::string LLFloaterIMSessionTab::appendTime() { - std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:" - "[" + LLTrans::getString("TimeMin") + "]"; + std::string timeStr; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr = "[" + LLTrans::getString("TimeHour") + "]:" + "[" + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr = "[" + LLTrans::getString("TimeHour12") + "]:" + "[" + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32)time_corrected(); diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 4f993ca0e1..c0fe7ad896 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -220,7 +220,8 @@ void LLFloaterInspect::refresh() } time_t timestamp = (time_t) (obj->mCreationDate/1000000); - std::string timeStr = getString("timeStamp"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("timeStamp") : getString("timeStampAMPM"); LLSD substitution; substitution["datetime"] = (S32) timestamp; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 52a3e78d04..5c5219bcdd 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -733,7 +733,8 @@ void LLPanelLandGeneral::refresh() // Display claim date time_t claim_date = parcel->getClaimDate(); - std::string claim_date_str = getString("time_stamp_template"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string claim_date_str = use_24h ? getString("time_stamp_template") : getString("time_stamp_template_ampm"); LLSD substitution; substitution["datetime"] = (S32) claim_date; LLStringUtil::format (claim_date_str, substitution); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 291f22d78f..734cc73331 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -475,6 +475,8 @@ bool LLFloaterPreference::postBuild() getChild("log_path_string")->setEnabled(false); // make it read-only but selectable getChild("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + mTimeFormatCombobox = getChild("time_format_combobox"); + mTimeFormatCombobox->setCommitCallback(boost::bind(&LLFloaterPreference::onTimeFormatChange, this)); getChild("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); getChild("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); @@ -1103,6 +1105,13 @@ void LLFloaterPreference::onLanguageChange() } } +void LLFloaterPreference::onTimeFormatChange() +{ + std::string val = mTimeFormatCombobox->getValue(); + gSavedSettings.setBOOL("Use24HourClock", val == "1"); + onLanguageChange(); +} + void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) { mNotificationOptions[OptionName] = getChild(OptionName)->getSelectedItemLabel(); @@ -1318,6 +1327,8 @@ void LLFloaterPreference::refresh() advanced->refresh(); } updateClickActionViews(); + + mTimeFormatCombobox->selectByValue(gSavedSettings.getBOOL("Use24HourClock") ? "1" : "0"); } void LLFloaterPreference::onCommitWindowedMode() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 61ac0a4d77..3150054798 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -117,6 +117,7 @@ protected: void onClickClearCache(); // Clear viewer texture cache, file cache on next startup void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above void onLanguageChange(); + void onTimeFormatChange(); void onNotificationsChange(const std::string& OptionName); void onNameTagOpacityChange(const LLSD& newvalue); @@ -235,6 +236,7 @@ private: LLButton* mDeleteTranscriptsBtn = nullptr; LLButton* mEnablePopupBtn = nullptr; LLButton* mDisablePopupBtn = nullptr; + LLComboBox* mTimeFormatCombobox = nullptr; std::unique_ptr< ll::prefs::SearchData > mSearchData; bool mSearchDataDirty; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index fa1f650113..30ea255e24 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -80,8 +80,8 @@ const static std::string MULTI_LINE_PREFIX(" "); * * Note: "You" was used as an avatar names in viewers of previous versions */ -const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); -const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*"); +const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); +const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]|\\[\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]).*"); /** * Regular expression suitable to match names like @@ -152,6 +152,10 @@ public: void checkAndCutOffDate(std::string& time_str) { + if (time_str.size() < 10) // not enough space for a date + { + return; + } // Cuts off the "%Y/%m/%d" from string for todays timestamps. // Assume that passed string has at least "%H:%M" time format. date log_date(not_a_date_time); @@ -168,20 +172,12 @@ public: if ( days_alive == zero_days ) { - // Yep, today's so strip "%Y/%m/%d" info - ptime stripped_time(not_a_date_time); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << time_str; - mTimeStream >> stripped_time; - mTimeStream.clear(); - - time_str.clear(); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << stripped_time; - mTimeStream >> time_str; - mTimeStream.clear(); + size_t pos = time_str.find_first_of(' '); + if (pos != std::string::npos) + { + time_str.erase(0, pos + 1); + LLStringUtil::trim(time_str); + } } LL_DEBUGS("LLChatLogParser") @@ -310,16 +306,22 @@ std::string LLLogChat::timestamp2LogString(U32 timestamp, bool withdate) std::string timeStr; if (withdate) { - timeStr = "[" + LLTrans::getString ("TimeYear") + "]/[" - + LLTrans::getString ("TimeMonth") + "]/[" - + LLTrans::getString ("TimeDay") + "] [" - + LLTrans::getString ("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin") + "]"; + timeStr = "[" + LLTrans::getString("TimeYear") + "]/[" + + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "] "; + } + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; } else { - timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin")+"]"; + timeStr += "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; } LLSD substitution; diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp index 5b8b28ebe6..9a33bcb1b9 100644 --- a/indra/newview/llnotificationlistitem.cpp +++ b/indra/newview/llnotificationlistitem.cpp @@ -38,6 +38,7 @@ #include "lluicolortable.h" #include "message.h" #include "llnotificationsutil.h" +#include "llviewercontrol.h" #include LLNotificationListItem::LLNotificationListItem(const Params& p) : LLPanel(p), @@ -133,10 +134,22 @@ std::string LLNotificationListItem::buildNotificationDate(const LLDate& time_sta default: timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" +LLTrans::getString("TimeDay")+"]/[" - +LLTrans::getString("TimeYear")+"] [" - +LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] [" - +LLTrans::getString("TimeTimezone")+"]"; + +LLTrans::getString("TimeYear")+"] ["; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } break; } LLSD substitution; diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 41373cd7f5..7596c0eba8 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -41,6 +41,7 @@ #include "lllandmarkactions.h" #include "llparcel.h" #include "llslurl.h" +#include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -326,7 +327,8 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) } else { - std::string timeStr = getString("acquired_date"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquired_date") : getString("acquired_date_ampm"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 902412d359..bfdfa68e01 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -42,6 +42,7 @@ #include "llnotificationsutil.h" #include "lltextbox.h" #include "lltoggleablemenu.h" +#include "llviewercontrol.h" #include "llviewermenu.h" #include "lllandmarkactions.h" #include "llclipboard.h" @@ -215,8 +216,18 @@ std::string LLTeleportHistoryFlatItem::getTimestamp() // Only show timestamp for today and yesterday if(time_diff < seconds_today + seconds_in_day) { - timestamp = "[" + LLTrans::getString("TimeHour12")+"]:[" - + LLTrans::getString("TimeMin")+"] ["+ LLTrans::getString("TimeAMPM")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timestamp = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timestamp = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + LLTrans::getString("TimeAMPM") + "]"; + } + LLSD substitution; substitution["datetime"] = (S32) date.secondsSinceEpoch(); LLStringUtil::format(timestamp, substitution); diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index 3d4b4fb9c1..6d50f216f5 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -487,7 +487,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } else { - std::string timeStr = getString("acquiredDate"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquiredDate") : getString("acquiredDateAMPM"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 3c3440d41a..95653dc19b 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -87,10 +87,21 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi std::string timeStr = "[" + LLTrans::getString("TimeWeek") + "], [" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" - + LLTrans::getString("TimeYear") + "] [" - + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString("TimeMin") + "] [" - + LLTrans::getString("TimeTimezone") + "]"; + + LLTrans::getString("TimeYear") + "] ["; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } const LLDate timeStamp = notification->getDate(); LLDate notice_date = timeStamp.notNull() ? timeStamp : payload["received_time"].asDate(); diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 7962c28e6d..153bee3aef 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -32,6 +32,7 @@ #include "message.h" #include "lltracker.h" #include "lluistring.h" +#include "llviewercontrol.h" #include "llviewertexturelist.h" #include "lltrans.h" #include "llgltexture.h" @@ -492,9 +493,20 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& case MAP_ITEM_MATURE_EVENT: case MAP_ITEM_ADULT_EVENT: { - std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"] [" - +LLTrans::getString ("TimeAMPM")+"]"; + std::string timeStr; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32) extra; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml index d52845160b..e5ad86b315 100644 --- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml @@ -15,6 +15,9 @@ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + (Beholdning) diff --git a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml index 168bb14248..3570ccacd2 100644 --- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml @@ -21,6 +21,9 @@ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + (Inventar) diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 508aba6ae1..c5b42b6dae 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -125,6 +125,9 @@ name="no_selection_text"> No parcel selected. + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml index 9403d58441..a083683c23 100644 --- a/indra/newview/skins/default/xui/en/floater_inspect.xml +++ b/indra/newview/skins/default/xui/en/floater_inspect.xml @@ -11,6 +11,10 @@ save_rect="true" title="INSPECT OBJECTS" width="400"> + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 7fc96af55d..fa3fd86d59 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1670,7 +1670,7 @@ The new skin will appear after you restart [APP_NAME]. icon="alertmodal.tga" name="ChangeLanguage" type="alertmodal"> -Changing language will take effect after you restart [APP_NAME]. +Changing language or time format will take effect after you restart [APP_NAME]. Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index 8f5292c531..bb877080b1 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -88,6 +88,10 @@ name="server_forbidden_text"> Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index 101c506309..ddddb4855f 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -91,6 +91,37 @@ name="Traditional Chinese" value="zh" /> + + Time Format: + + + + + Owner can: + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index d5bf189d82..60172b3960 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -32,6 +32,7 @@ #include "lltrans.h" #include "lluistring.h" #include "../llviewertexture.h" +#include "../llviewercontrol.h" #include "../llworldmapmessage.h" // Class to test #include "../llworldmap.h" @@ -71,6 +72,11 @@ void LLUIString::updateResult() const { } void LLUIString::setArg(const std::string& , const std::string& ) { } void LLUIString::assign(const std::string& ) { } +LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name) {} +LLControlGroup::~LLControlGroup() {} +bool LLControlGroup::getBOOL(std::string_view) { return true; } +LLControlGroup gSavedSettings("test_settings"); + // End Stubbing // ------------------------------------------------------------------------------------------- -- cgit v1.3