From c285f59ce2a05703e3a1232fcaf3ee3aea714b3f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sun, 18 Feb 2024 12:52:19 +0100 Subject: Replace BOOL with bool in llwindow and dependent classes --- indra/newview/llchathistory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 43dc10ef5f..707b1ceda8 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -155,7 +155,7 @@ public: } } - BOOL handleMouseUp(S32 x, S32 y, MASK mask) + bool handleMouseUp(S32 x, S32 y, MASK mask) { return LLPanel::handleMouseUp(x,y,mask); } @@ -619,12 +619,12 @@ public: return child->pointInView(local_x, local_y); } - BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) + bool handleRightMouseDown(S32 x, S32 y, MASK mask) { if(pointInChild("avatar_icon",x,y) || pointInChild("user_name",x,y)) { showContextMenu(x,y); - return TRUE; + return true; } return LLPanel::handleRightMouseDown(x,y,mask); -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/newview/llchathistory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 707b1ceda8..fbcff0fc5a 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -577,7 +577,7 @@ public: return false; } - BOOL postBuild() + bool postBuild() { setDoubleClickCallback(boost::bind(&LLChatHistoryHeader::showInspector, this)); -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/llchathistory.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index fbcff0fc5a..e1140e77fd 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -90,7 +90,7 @@ public: } LLUUID object_id; - if (!object_id.set(params[0], FALSE)) + if (!object_id.set(params[0], false)) { return false; } @@ -591,7 +591,7 @@ public: if (mInfoCtrl) { mInfoCtrl->setCommitCallback(boost::bind(&LLChatHistoryHeader::onClickInfoCtrl, mInfoCtrl)); - mInfoCtrl->setVisible(FALSE); + mInfoCtrl->setVisible(false); } else { @@ -695,7 +695,7 @@ public: updateMinUserNameWidth(); LLColor4 sep_color = LLUIColorTable::instance().getColor("ChatTeleportSeparatorColor"); setTransparentColor(sep_color); - mTimeBoxTextBox->setVisible(FALSE); + mTimeBoxTextBox->setVisible(false); } else if (chat.mFromName.empty() || mSourceType == CHAT_SOURCE_SYSTEM) @@ -744,7 +744,7 @@ public: style_params_name.font.name("SansSerifSmall"); style_params_name.font.style("NORMAL"); style_params_name.readonly_color(userNameColor); - user_name->appendText(" - " + username, FALSE, style_params_name); + user_name->appendText(" - " + username, false, style_params_name); } } else @@ -830,7 +830,7 @@ public: user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight()); user_name->setRect(user_name_rect); - time_box->setVisible(TRUE); + time_box->setVisible(true); } LLPanel::draw(); @@ -982,7 +982,7 @@ protected: void hideInfoCtrl() { - mInfoCtrl->setVisible(FALSE); + mInfoCtrl->setVisible(false); } private: @@ -1042,7 +1042,7 @@ private: style_params_name.font.name("SansSerifSmall"); style_params_name.font.style("NORMAL"); style_params_name.readonly_color(userNameColor); - user_name->appendText(" - " + av_name.getUserName(), FALSE, style_params_name); + user_name->appendText(" - " + av_name.getUserName(), false, style_params_name); } setToolTip( av_name.getUserName() ); // name might have changed, update width @@ -1223,7 +1223,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL if (mNotifyAboutUnreadMsg && !mEditor->scrolledToEnd() && !from_me && !chat.mFromName.empty()) { mUnreadChatSources.insert(chat.mFromName); - mMoreChatPanel->setVisible(TRUE); + mMoreChatPanel->setVisible(true); std::string chatters; for (unread_chat_source_t::iterator it = mUnreadChatSources.begin(); it != mUnreadChatSources.end();) @@ -1561,7 +1561,7 @@ void LLChatHistory::draw() if (mEditor->scrolledToEnd()) { mUnreadChatSources.clear(); - mMoreChatPanel->setVisible(FALSE); + mMoreChatPanel->setVisible(false); } LLUICtrl::draw(); -- cgit v1.2.3 From a865d423974ea06dffa47798c81e98e7570b02ec Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Tue, 5 Mar 2024 17:03:11 +0100 Subject: viewer#819 Avoid reading the same XML file multiple times --- indra/newview/llchathistory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index e1140e77fd..0b7cdd1db4 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -131,7 +131,7 @@ public: static LLChatHistoryHeader* createInstance(const std::string& file_name) { LLChatHistoryHeader* pInstance = new LLChatHistoryHeader; - pInstance->buildFromFile(file_name); + pInstance->buildFromFile(file_name, true); return pInstance; } @@ -587,7 +587,7 @@ public: mUserNameTextBox = getChild("user_name"); mTimeBoxTextBox = getChild("time_box"); - mInfoCtrl = LLUICtrlFactory::getInstance()->createFromFile("inspector_info_ctrl.xml", this, LLPanel::child_registry_t::instance()); + mInfoCtrl = LLUICtrlFactory::getInstance()->createFromFile("inspector_info_ctrl.xml", this, LLPanel::child_registry_t::instance(), true); if (mInfoCtrl) { mInfoCtrl->setCommitCallback(boost::bind(&LLChatHistoryHeader::onClickInfoCtrl, mInfoCtrl)); @@ -1179,7 +1179,7 @@ void LLChatHistory::initFromParams(const LLChatHistory::Params& p) LLView* LLChatHistory::getSeparator() { - LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance()); + LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance(), true); return separator; } -- cgit v1.2.3 From 2008f87f10d51a2f9372aa4a4d72e86ac94e1e81 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 13 May 2024 18:26:53 +0300 Subject: Revert "viewer#819 Avoid reading the same XML file multiple times" This reverts commit a865d423974ea06dffa47798c81e98e7570b02ec. Reason for revert: viewer#1420, reverting to not hold maint-A (is deepCopy not full?) --- indra/newview/llchathistory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 0c365d33c4..7d9fa2a475 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -131,7 +131,7 @@ public: static LLChatHistoryHeader* createInstance(const std::string& file_name) { LLChatHistoryHeader* pInstance = new LLChatHistoryHeader; - pInstance->buildFromFile(file_name, true); + pInstance->buildFromFile(file_name); return pInstance; } @@ -587,7 +587,7 @@ public: mUserNameTextBox = getChild("user_name"); mTimeBoxTextBox = getChild("time_box"); - mInfoCtrl = LLUICtrlFactory::getInstance()->createFromFile("inspector_info_ctrl.xml", this, LLPanel::child_registry_t::instance(), true); + mInfoCtrl = LLUICtrlFactory::getInstance()->createFromFile("inspector_info_ctrl.xml", this, LLPanel::child_registry_t::instance()); if (mInfoCtrl) { mInfoCtrl->setCommitCallback(boost::bind(&LLChatHistoryHeader::onClickInfoCtrl, mInfoCtrl)); @@ -1181,7 +1181,7 @@ void LLChatHistory::initFromParams(const LLChatHistory::Params& p) LLView* LLChatHistory::getSeparator() { - LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance(), true); + LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance()); return separator; } -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llchathistory.cpp | 3096 +++++++++++++++++++-------------------- 1 file changed, 1548 insertions(+), 1548 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 75f3cb11a8..3893b21c2a 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1,1548 +1,1548 @@ -/** - * @file llchathistory.cpp - * @brief LLTextEditor base class - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llchathistory.h" - -#include - -#include "llavatarnamecache.h" -#include "llinstantmessage.h" - -#include "llimview.h" -#include "llcommandhandler.h" -#include "llpanel.h" -#include "lluictrlfactory.h" -#include "llscrollcontainer.h" -#include "llagent.h" -#include "llagentdata.h" -#include "llavataractions.h" -#include "llavatariconctrl.h" -#include "llcallingcard.h" //for LLAvatarTracker -#include "llgroupactions.h" -#include "llgroupmgr.h" -#include "llspeakers.h" //for LLIMSpeakerMgr -#include "lltrans.h" -#include "llfloaterreg.h" -#include "llfloaterreporter.h" -#include "llfloatersidepanelcontainer.h" -#include "llmutelist.h" -#include "llstylemap.h" -#include "llslurl.h" -#include "lllayoutstack.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" -#include "lltoastnotifypanel.h" -#include "lltooltip.h" -#include "llviewerregion.h" -#include "llviewertexteditor.h" -#include "llworld.h" -#include "lluiconstants.h" -#include "llstring.h" -#include "llurlaction.h" -#include "llviewercontrol.h" -#include "llviewermenu.h" -#include "llviewerobjectlist.h" - -static LLDefaultChildRegistry::Register r("chat_history"); - -const static std::string NEW_LINE(rawstr_to_utf8("\n")); - -const static std::string SLURL_APP_AGENT = "secondlife:///app/agent/"; -const static std::string SLURL_ABOUT = "/about"; - -// support for secondlife:///app/objectim/{UUID}/ SLapps -class LLObjectIMHandler : public LLCommandHandler -{ -public: - // requests will be throttled from a non-trusted browser - LLObjectIMHandler() : LLCommandHandler("objectim", UNTRUSTED_THROTTLE) {} - - bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) - { - if (params.size() < 1) - { - return false; - } - - LLUUID object_id; - if (!object_id.set(params[0], false)) - { - return false; - } - - LLSD payload; - payload["object_id"] = object_id; - payload["owner_id"] = query_map["owner"]; - payload["name"] = query_map["name"]; - payload["slurl"] = LLWeb::escapeURL(query_map["slurl"]); - payload["group_owned"] = query_map["groupowned"]; - LLFloaterReg::showInstance("inspect_remote_object", payload); - return true; - } -}; -LLObjectIMHandler gObjectIMHandler; - -class LLChatHistoryHeader: public LLPanel -{ -public: - LLChatHistoryHeader() - : LLPanel(), - mInfoCtrl(NULL), - mPopupMenuHandleAvatar(), - mPopupMenuHandleObject(), - mAvatarID(), - mSourceType(CHAT_SOURCE_UNKNOWN), - mFrom(), - mSessionID(), - mCreationTime(time_corrected()), - mMinUserNameWidth(0), - mUserNameFont(NULL), - mUserNameTextBox(NULL), - mTimeBoxTextBox(NULL), - mNeedsTimeBox(true), - mAvatarNameCacheConnection() - {} - - static LLChatHistoryHeader* createInstance(const std::string& file_name) - { - LLChatHistoryHeader* pInstance = new LLChatHistoryHeader; - pInstance->buildFromFile(file_name); - return pInstance; - } - - ~LLChatHistoryHeader() - { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - auto menu = mPopupMenuHandleAvatar.get(); - if (menu) - { - menu->die(); - mPopupMenuHandleAvatar.markDead(); - } - menu = mPopupMenuHandleObject.get(); - if (menu) - { - menu->die(); - mPopupMenuHandleObject.markDead(); - } - } - - bool handleMouseUp(S32 x, S32 y, MASK mask) - { - return LLPanel::handleMouseUp(x,y,mask); - } - - void onObjectIconContextMenuItemClicked(const LLSD& userdata) - { - std::string level = userdata.asString(); - - if (level == "profile") - { - LLFloaterReg::showInstance("inspect_remote_object", mObjectData); - } - else if (level == "block") - { - LLMuteList::getInstance()->add(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); - - LLFloaterSidePanelContainer::showPanel("people", "panel_people", - LLSD().with("people_panel_tab_name", "blocked_panel").with("blocked_to_select", getAvatarId())); - } - else if (level == "unblock") - { - LLMuteList::getInstance()->remove(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); - } - else if (level == "map") - { - std::string url = "secondlife://" + mObjectData["slurl"].asString(); - LLUrlAction::showLocationOnMap(url); - } - else if (level == "teleport") - { - std::string url = "secondlife://" + mObjectData["slurl"].asString(); - LLUrlAction::teleportToLocation(url); - } - - } - - bool onObjectIconContextMenuItemVisible(const LLSD& userdata) - { - std::string level = userdata.asString(); - if (level == "is_blocked") - { - return LLMuteList::getInstance()->isMuted(getAvatarId(), mFrom, LLMute::flagTextChat); - } - else if (level == "not_blocked") - { - return !LLMuteList::getInstance()->isMuted(getAvatarId(), mFrom, LLMute::flagTextChat); - } - return false; - } - - void banGroupMember(const LLUUID& participant_uuid) - { - LLUUID group_uuid = mSessionID; - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); - if (!gdatap) - { - // Not a group - return; - } - - gdatap->banMemberById(participant_uuid); - } - - bool canBanInGroup() - { - LLUUID group_uuid = mSessionID; - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); - if (!gdatap) - { - // Not a group - return false; - } - - if (gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) - && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS)) - { - return true; - } - - return false; - } - - bool canBanGroupMember(const LLUUID& participant_uuid) - { - LLUUID group_uuid = mSessionID; - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); - if (!gdatap) - { - // Not a group - return false; - } - - if (gdatap->mPendingBanRequest) - { - return false; - } - - if (gAgentID == getAvatarId()) - { - //Don't ban self - return false; - } - - if (gdatap->isRoleMemberDataComplete()) - { - if (gdatap->mMembers.size()) - { - LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find(participant_uuid); - if (mi != gdatap->mMembers.end()) - { - LLGroupMemberData* member_data = (*mi).second; - // Is the member an owner? - if (member_data && member_data->isInRole(gdatap->mOwnerRole)) - { - return false; - } - - if (gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) - && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS)) - { - return true; - } - } - } - } - - LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - LLSpeaker * speakerp = speaker_mgr->findSpeaker(participant_uuid).get(); - - if (speakerp - && gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) - && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS)) - { - return true; - } - } - - return false; - } - - bool isGroupModerator() - { - LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (!speaker_mgr) - { - LL_WARNS() << "Speaker manager is missing" << LL_ENDL; - return false; - } - - // Is session a group call/chat? - if(gAgent.isInGroup(mSessionID)) - { - LLSpeaker * speakerp = speaker_mgr->findSpeaker(gAgentID).get(); - - // Is agent a moderator? - return speakerp && speakerp->mIsModerator; - } - - return false; - } - - bool canModerate(const std::string& userdata) - { - // only group moderators can perform actions related to this "enable callback" - if (!isGroupModerator() || gAgentID == getAvatarId()) - { - return false; - } - - LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (!speaker_mgr) - { - return false; - } - - LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()).get(); - if (!speakerp) - { - return false; - } - - bool voice_channel = speakerp->isInVoiceChannel(); - - if ("can_moderate_voice" == userdata) - { - return voice_channel; - } - else if ("can_mute" == userdata) - { - return voice_channel && (speakerp->mStatus != LLSpeaker::STATUS_MUTED); - } - else if ("can_unmute" == userdata) - { - return speakerp->mStatus == LLSpeaker::STATUS_MUTED; - } - else if ("can_allow_text_chat" == userdata) - { - return true; - } - - return false; - } - - void onAvatarIconContextMenuItemClicked(const LLSD& userdata) - { - std::string level = userdata.asString(); - - if (level == "profile") - { - LLAvatarActions::showProfile(getAvatarId()); - } - else if (level == "im") - { - LLAvatarActions::startIM(getAvatarId()); - } - else if (level == "teleport") - { - LLAvatarActions::offerTeleport(getAvatarId()); - } - else if (level == "request_teleport") - { - LLAvatarActions::teleportRequest(getAvatarId()); - } - else if (level == "voice_call") - { - LLAvatarActions::startCall(getAvatarId()); - } - else if (level == "chat_history") - { - LLAvatarActions::viewChatHistory(getAvatarId()); - } - else if (level == "add") - { - LLAvatarActions::requestFriendshipDialog(getAvatarId(), mFrom); - } - else if (level == "remove") - { - LLAvatarActions::removeFriendDialog(getAvatarId()); - } - else if (level == "invite_to_group") - { - LLAvatarActions::inviteToGroup(getAvatarId()); - } - else if (level == "zoom_in") - { - handle_zoom_to_object(getAvatarId()); - } - else if (level == "map") - { - LLAvatarActions::showOnMap(getAvatarId()); - } - else if (level == "share") - { - LLAvatarActions::share(getAvatarId()); - } - else if (level == "pay") - { - LLAvatarActions::pay(getAvatarId()); - } - else if (level == "report_abuse") - { - std::string time_string; - if (mTime > 0) // have frame time - { - time_t current_time = time_corrected(); - time_t message_time = current_time - LLFrameTimer::getElapsedSeconds() + mTime; - - time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" - + LLTrans::getString("TimeDay") + "]/[" - + LLTrans::getString("TimeYear") + "] [" - + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString("TimeMin") + "]"; - - LLSD substitution; - - substitution["datetime"] = (S32)message_time; - LLStringUtil::format(time_string, substitution); - } - else - { - // From history. This might be empty or not full. - // See LLChatLogParser::parse - time_string = getChild("time_box")->getValue().asString(); - - // Just add current date if not full. - // Should be fine since both times are supposed to be stl - if (!time_string.empty() && time_string.size() < 7) - { - time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" - + LLTrans::getString("TimeDay") + "]/[" - + LLTrans::getString("TimeYear") + "] " + time_string; - - LLSD substitution; - // To avoid adding today's date to yesterday's timestamp, - // use creation time instead of current time - substitution["datetime"] = (S32)mCreationTime; - LLStringUtil::format(time_string, substitution); - } - } - LLFloaterReporter::showFromChat(mAvatarID, mFrom, time_string, mText); - } - else if(level == "block_unblock") - { - LLAvatarActions::toggleMute(getAvatarId(), LLMute::flagVoiceChat); - } - else if(level == "mute_unmute") - { - LLAvatarActions::toggleMute(getAvatarId(), LLMute::flagTextChat); - } - else if(level == "toggle_allow_text_chat") - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - speaker_mgr->toggleAllowTextChat(getAvatarId()); - } - else if(level == "group_mute") - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - speaker_mgr->moderateVoiceParticipant(getAvatarId(), false); - } - } - else if(level == "group_unmute") - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - speaker_mgr->moderateVoiceParticipant(getAvatarId(), true); - } - } - else if(level == "ban_member") - { - banGroupMember(getAvatarId()); - } - } - - bool onAvatarIconContextMenuItemChecked(const LLSD& userdata) - { - std::string level = userdata.asString(); - - if (level == "is_blocked") - { - return LLMuteList::getInstance()->isMuted(getAvatarId(), LLMute::flagVoiceChat); - } - if (level == "is_muted") - { - return LLMuteList::getInstance()->isMuted(getAvatarId(), LLMute::flagTextChat); - } - else if (level == "is_allowed_text_chat") - { - if (gAgent.isInGroup(mSessionID)) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if(speaker_mgr) - { - const LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()); - if (NULL != speakerp) - { - return !speakerp->mModeratorMutedText; - } - } - } - return false; - } - return false; - } - - bool onAvatarIconContextMenuItemEnabled(const LLSD& userdata) - { - std::string level = userdata.asString(); - - if (level == "can_allow_text_chat" || level == "can_mute" || level == "can_unmute") - { - return canModerate(userdata); - } - else if (level == "report_abuse") - { - return gAgentID != mAvatarID; - } - else if (level == "can_ban_member") - { - return canBanGroupMember(getAvatarId()); - } - return false; - } - - bool onAvatarIconContextMenuItemVisible(const LLSD& userdata) - { - std::string level = userdata.asString(); - - if (level == "show_mute") - { - LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()).get(); - if (speakerp) - { - return speakerp->isInVoiceChannel() && speakerp->mStatus != LLSpeaker::STATUS_MUTED; - } - } - return false; - } - else if (level == "show_unmute") - { - LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()).get(); - if (speakerp) - { - return speakerp->mStatus == LLSpeaker::STATUS_MUTED; - } - } - return false; - } - return false; - } - - bool postBuild() - { - setDoubleClickCallback(boost::bind(&LLChatHistoryHeader::showInspector, this)); - - setMouseEnterCallback(boost::bind(&LLChatHistoryHeader::showInfoCtrl, this)); - setMouseLeaveCallback(boost::bind(&LLChatHistoryHeader::hideInfoCtrl, this)); - - mUserNameTextBox = getChild("user_name"); - mTimeBoxTextBox = getChild("time_box"); - - mInfoCtrl = LLUICtrlFactory::getInstance()->createFromFile("inspector_info_ctrl.xml", this, LLPanel::child_registry_t::instance()); - if (mInfoCtrl) - { - mInfoCtrl->setCommitCallback(boost::bind(&LLChatHistoryHeader::onClickInfoCtrl, mInfoCtrl)); - mInfoCtrl->setVisible(false); - } - else - { - LL_ERRS() << "Failed to create an interface element due to missing or corrupted file inspector_info_ctrl.xml" << LL_ENDL; - } - - return LLPanel::postBuild(); - } - - bool pointInChild(const std::string& name,S32 x,S32 y) - { - LLUICtrl* child = findChild(name); - if(!child) - return false; - - LLView* parent = child->getParent(); - if(parent!=this) - { - x-=parent->getRect().mLeft; - y-=parent->getRect().mBottom; - } - - S32 local_x = x - child->getRect().mLeft ; - S32 local_y = y - child->getRect().mBottom ; - return child->pointInView(local_x, local_y); - } - - bool handleRightMouseDown(S32 x, S32 y, MASK mask) - { - if(pointInChild("avatar_icon",x,y) || pointInChild("user_name",x,y)) - { - showContextMenu(x,y); - return true; - } - - return LLPanel::handleRightMouseDown(x,y,mask); - } - - void showInspector() - { - if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType && CHAT_SOURCE_REGION != mSourceType) return; - - if (mSourceType == CHAT_SOURCE_OBJECT) - { - LLFloaterReg::showInstance("inspect_remote_object", mObjectData); - } - else if (mSourceType == CHAT_SOURCE_AGENT) - { - LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarID)); - } - //if chat source is system, you may add "else" here to define behaviour. - } - - static void onClickInfoCtrl(LLUICtrl* info_ctrl) - { - if (!info_ctrl) return; - - LLChatHistoryHeader* header = dynamic_cast(info_ctrl->getParent()); - if (!header) return; - - header->showInspector(); - } - - - const LLUUID& getAvatarId () const { return mAvatarID;} - - void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args) - { - mAvatarID = chat.mFromID; - mSessionID = chat.mSessionID; - mSourceType = chat.mSourceType; - - // To be able to report a message, we need a copy of it's text - // and it's easier to store text directly than trying to get - // it from a lltextsegment or chat's mEditor - mText = chat.mText; - mTime = chat.mTime; - - //*TODO overly defensive thing, source type should be maintained out there - if((chat.mFromID.isNull() && chat.mFromName.empty()) || (chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull())) - { - mSourceType = CHAT_SOURCE_SYSTEM; - } - - mUserNameFont = style_params.font(); - if (!mUserNameTextBox) - { - mUserNameTextBox = getChild("user_name"); - mTimeBoxTextBox = getChild("time_box"); - } - LLTextBox* user_name = mUserNameTextBox; - user_name->setReadOnlyColor(style_params.readonly_color()); - user_name->setColor(style_params.color()); - - if (mSourceType == CHAT_SOURCE_TELEPORT - && chat.mChatStyle == CHAT_STYLE_TELEPORT_SEP) - { - mFrom = chat.mFromName; - mNeedsTimeBox = false; - user_name->setValue(mFrom); - updateMinUserNameWidth(); - LLColor4 sep_color = LLUIColorTable::instance().getColor("ChatTeleportSeparatorColor"); - setTransparentColor(sep_color); - mTimeBoxTextBox->setVisible(false); - } - else if (chat.mFromName.empty() - || mSourceType == CHAT_SOURCE_SYSTEM) - { - mFrom = LLTrans::getString("SECOND_LIFE"); - if(!chat.mFromName.empty() && (mFrom != chat.mFromName)) - { - mFrom += " (" + chat.mFromName + ")"; - } - user_name->setValue(mFrom); - updateMinUserNameWidth(); - } - else if (mSourceType == CHAT_SOURCE_AGENT - && !mAvatarID.isNull() - && chat.mChatStyle != CHAT_STYLE_HISTORY) - { - // ...from a normal user, lookup the name and fill in later. - // *NOTE: Do not do this for chat history logs, otherwise the viewer - // will flood the People API with lookup requests on startup - - // Start with blank so sample data from XUI XML doesn't - // flash on the screen - user_name->setValue( LLSD() ); - fetchAvatarName(); - } - else if (chat.mChatStyle == CHAT_STYLE_HISTORY || - mSourceType == CHAT_SOURCE_AGENT) - { - //if it's an avatar name with a username add formatting - S32 username_start = chat.mFromName.rfind(" ("); - S32 username_end = chat.mFromName.rfind(')'); - - if (username_start != std::string::npos && - username_end == (chat.mFromName.length() - 1)) - { - mFrom = chat.mFromName.substr(0, username_start); - user_name->setValue(mFrom); - - if (gSavedSettings.getBOOL("NameTagShowUsernames")) - { - std::string username = chat.mFromName.substr(username_start + 2); - username = username.substr(0, username.length() - 1); - LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); - style_params_name.color(userNameColor); - style_params_name.font.name("SansSerifSmall"); - style_params_name.font.style("NORMAL"); - style_params_name.readonly_color(userNameColor); - user_name->appendText(" - " + username, false, style_params_name); - } - } - else - { - mFrom = chat.mFromName; - user_name->setValue(mFrom); - updateMinUserNameWidth(); - } - } - else - { - // ...from an object, just use name as given - mFrom = chat.mFromName; - user_name->setValue(mFrom); - updateMinUserNameWidth(); - } - - - setTimeField(chat); - - // Set up the icon. - LLAvatarIconCtrl* icon = getChild("avatar_icon"); - - if(mSourceType != CHAT_SOURCE_AGENT || mAvatarID.isNull()) - icon->setDrawTooltip(false); - - switch (mSourceType) - { - case CHAT_SOURCE_AGENT: - icon->setValue(chat.mFromID); - break; - case CHAT_SOURCE_OBJECT: - icon->setValue(LLSD("OBJECT_Icon")); - break; - case CHAT_SOURCE_SYSTEM: - case CHAT_SOURCE_REGION: - icon->setValue(LLSD("SL_Logo")); - break; - case CHAT_SOURCE_TELEPORT: - icon->setValue(LLSD("Command_Destinations_Icon")); - break; - case CHAT_SOURCE_UNKNOWN: - icon->setValue(LLSD("Unknown_Icon")); - } - - // In case the message came from an object, save the object info - // to be able properly show its profile. - if ( chat.mSourceType == CHAT_SOURCE_OBJECT) - { - std::string slurl = args["slurl"].asString(); - if (slurl.empty() && LLWorld::instanceExists()) - { - LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent); - if(region) - { - LLSLURL region_slurl(region->getName(), chat.mPosAgent); - slurl = region_slurl.getLocationString(); - } - } - - LLSD payload; - payload["object_id"] = chat.mFromID; - payload["name"] = chat.mFromName; - payload["owner_id"] = chat.mOwnerID; - payload["slurl"] = LLWeb::escapeURL(slurl); - - mObjectData = payload; - } - } - - /*virtual*/ void draw() - { - LLTextBox* user_name = mUserNameTextBox; //getChild("user_name"); - LLTextBox* time_box = mTimeBoxTextBox; //getChild("time_box"); - - LLRect user_name_rect = user_name->getRect(); - S32 user_name_width = user_name_rect.getWidth(); - S32 time_box_width = time_box->getRect().getWidth(); - - if (mNeedsTimeBox && !time_box->getVisible() && user_name_width > mMinUserNameWidth) - { - user_name_rect.mRight -= time_box_width; - user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight()); - user_name->setRect(user_name_rect); - - time_box->setVisible(true); - } - - LLPanel::draw(); - } - - void updateMinUserNameWidth() - { - if (mUserNameFont) - { - LLTextBox* user_name = getChild("user_name"); - const LLWString& text = user_name->getWText(); - mMinUserNameWidth = mUserNameFont->getWidth(text.c_str()) + PADDING; - } - } - -protected: - static const S32 PADDING = 20; - - void showContextMenu(S32 x,S32 y) - { - if(mSourceType == CHAT_SOURCE_SYSTEM) - showSystemContextMenu(x,y); - if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_AGENT) - showAvatarContextMenu(x,y); - if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_OBJECT) - showObjectContextMenu(x,y); - } - - void showSystemContextMenu(S32 x,S32 y) - { - } - - void showObjectContextMenu(S32 x,S32 y) - { - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleObject.get(); - if (!menu) - { - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable; - registrar.add("ObjectIcon.Action", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemClicked, this, _2)); - registrar_enable.add("ObjectIcon.Visible", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemVisible, this, _2)); - - menu = LLUICtrlFactory::getInstance()->createFromFile("menu_object_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if (menu) - { - mPopupMenuHandleObject = menu->getHandle(); - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - } - else - { - LL_WARNS() << " Failed to create menu_object_icon.xml" << LL_ENDL; - } - } - else - { - LLMenuGL::showPopup(this, menu, x, y); - } - } - - void showAvatarContextMenu(S32 x,S32 y) - { - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleAvatar.get(); - if (!menu) - { - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable; - registrar.add("AvatarIcon.Action", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemClicked, this, _2)); - registrar_enable.add("AvatarIcon.Check", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemChecked, this, _2)); - registrar_enable.add("AvatarIcon.Enable", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemEnabled, this, _2)); - registrar_enable.add("AvatarIcon.Visible", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemVisible, this, _2)); - - menu = LLUICtrlFactory::getInstance()->createFromFile("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if (menu) - { - mPopupMenuHandleAvatar = menu->getHandle(); - } - else - { - LL_WARNS() << " Failed to create menu_avatar_icon.xml" << LL_ENDL; - } - } - - if(menu) - { - bool is_friend = LLAvatarActions::isFriend(mAvatarID); - bool is_group_session = gAgent.isInGroup(mSessionID); - - menu->setItemEnabled("Add Friend", !is_friend); - menu->setItemEnabled("Remove Friend", is_friend); - menu->setItemVisible("Moderator Options Separator", is_group_session && isGroupModerator()); - menu->setItemVisible("Moderator Options", is_group_session && isGroupModerator()); - menu->setItemVisible("Group Ban Separator", is_group_session && canBanInGroup()); - menu->setItemVisible("BanMember", is_group_session && canBanInGroup()); - - if(gAgentID == mAvatarID) - { - menu->setItemEnabled("Add Friend", false); - menu->setItemEnabled("Send IM", false); - menu->setItemEnabled("Remove Friend", false); - menu->setItemEnabled("Offer Teleport",false); - menu->setItemEnabled("Request Teleport",false); - menu->setItemEnabled("Voice Call", false); - menu->setItemEnabled("Chat History", false); - menu->setItemEnabled("Invite Group", false); - menu->setItemEnabled("Zoom In", false); - menu->setItemEnabled("Share", false); - menu->setItemEnabled("Pay", false); - menu->setItemEnabled("Block Unblock", false); - menu->setItemEnabled("Mute Text", false); - } - else - { - LLUUID currentSessionID = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, mAvatarID); - if (mSessionID == currentSessionID) - { - menu->setItemVisible("Send IM", false); - } - menu->setItemEnabled("Offer Teleport", LLAvatarActions::canOfferTeleport(mAvatarID)); - menu->setItemEnabled("Request Teleport", LLAvatarActions::canOfferTeleport(mAvatarID)); - menu->setItemEnabled("Voice Call", LLAvatarActions::canCall()); - - // We should only show 'Zoom in' item in a nearby chat - bool should_show_zoom = !LLIMModel::getInstance()->findIMSession(currentSessionID); - menu->setItemVisible("Zoom In", should_show_zoom && gObjectList.findObject(mAvatarID)); - menu->setItemEnabled("Block Unblock", LLAvatarActions::canBlock(mAvatarID)); - menu->setItemEnabled("Mute Text", LLAvatarActions::canBlock(mAvatarID)); - menu->setItemEnabled("Chat History", LLLogChat::isTranscriptExist(mAvatarID)); - } - - menu->setItemEnabled("Map", (LLAvatarTracker::instance().isBuddyOnline(mAvatarID) && is_agent_mappable(mAvatarID)) || gAgent.isGodlike() ); - menu->buildDrawLabels(); - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - } - } - - void showInfoCtrl() - { - const bool isVisible = !mAvatarID.isNull() && !mFrom.empty() && CHAT_SOURCE_SYSTEM != mSourceType && CHAT_SOURCE_REGION != mSourceType; - if (isVisible) - { - const LLRect sticky_rect = mUserNameTextBox->getRect(); - S32 icon_x = llmin(sticky_rect.mLeft + mUserNameTextBox->getTextBoundingRect().getWidth() + 7, sticky_rect.mRight - 3); - mInfoCtrl->setOrigin(icon_x, sticky_rect.getCenterY() - mInfoCtrl->getRect().getHeight() / 2 ) ; - } - mInfoCtrl->setVisible(isVisible); - } - - void hideInfoCtrl() - { - mInfoCtrl->setVisible(false); - } - -private: - void setTimeField(const LLChat& chat) - { - LLTextBox* time_box = getChild("time_box"); - - LLRect rect_before = time_box->getRect(); - - time_box->setValue(chat.mTimeStr); - - // set necessary textbox width to fit all text - time_box->reshapeToFitText(); - LLRect rect_after = time_box->getRect(); - - // move rect to the left to correct position... - S32 delta_pos_x = rect_before.getWidth() - rect_after.getWidth(); - S32 delta_pos_y = rect_before.getHeight() - rect_after.getHeight(); - time_box->translate(delta_pos_x, delta_pos_y); - - //... & change width of the name control - LLView* user_name = getChild("user_name"); - const LLRect& user_rect = user_name->getRect(); - user_name->reshape(user_rect.getWidth() + delta_pos_x, user_rect.getHeight()); - } - - void fetchAvatarName() - { - if (mAvatarID.notNull()) - { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, - boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2)); - } - } - - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) - { - mAvatarNameCacheConnection.disconnect(); - - mFrom = av_name.getDisplayName(); - - LLTextBox* user_name = getChild("user_name"); - user_name->setValue( LLSD(av_name.getDisplayName() ) ); - user_name->setToolTip( av_name.getUserName() ); - - if (gSavedSettings.getBOOL("NameTagShowUsernames") && - av_name.useDisplayNames() && - !av_name.isDisplayNameDefault()) - { - LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); - style_params_name.color(userNameColor); - style_params_name.font.name("SansSerifSmall"); - style_params_name.font.style("NORMAL"); - style_params_name.readonly_color(userNameColor); - user_name->appendText(" - " + av_name.getUserName(), false, style_params_name); - } - setToolTip( av_name.getUserName() ); - // name might have changed, update width - updateMinUserNameWidth(); - } - -protected: - LLHandle mPopupMenuHandleAvatar; - LLHandle mPopupMenuHandleObject; - - LLUICtrl* mInfoCtrl; - - LLUUID mAvatarID; - LLSD mObjectData; - EChatSourceType mSourceType; - std::string mFrom; - LLUUID mSessionID; - std::string mText; - F64 mTime; // IM's frame time - time_t mCreationTime; // Views's time - - S32 mMinUserNameWidth; - const LLFontGL* mUserNameFont; - LLTextBox* mUserNameTextBox; - LLTextBox* mTimeBoxTextBox; - - bool mNeedsTimeBox; - -private: - boost::signals2::connection mAvatarNameCacheConnection; -}; - -LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) -: LLUICtrl(p), - mMessageHeaderFilename(p.message_header), - mMessageSeparatorFilename(p.message_separator), - mLeftTextPad(p.left_text_pad), - mRightTextPad(p.right_text_pad), - mLeftWidgetPad(p.left_widget_pad), - mRightWidgetPad(p.right_widget_pad), - mTopSeparatorPad(p.top_separator_pad), - mBottomSeparatorPad(p.bottom_separator_pad), - mTopHeaderPad(p.top_header_pad), - mBottomHeaderPad(p.bottom_header_pad), - mIsLastMessageFromLog(false), - mNotifyAboutUnreadMsg(p.notify_unread_msg) -{ - LLTextEditor::Params editor_params(p); - editor_params.rect = getLocalRect(); - editor_params.follows.flags = FOLLOWS_ALL; - editor_params.enabled = false; // read only - editor_params.show_context_menu = "true"; - editor_params.trusted_content = false; - editor_params.text_valign = LLFontGL::VAlign::VCENTER; - editor_params.use_color = true; - mEditor = LLUICtrlFactory::create(editor_params, this); - mEditor->setIsFriendCallback(LLAvatarActions::isFriend); - mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0)); - -} - -LLSD LLChatHistory::getValue() const -{ - return LLSD(mEditor->getText()); -} - -LLChatHistory::~LLChatHistory() -{ - this->clear(); -} - -void LLChatHistory::initFromParams(const LLChatHistory::Params& p) -{ - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - LLRect stack_rect = getLocalRect(); - stack_rect.mRight -= scrollbar_size; - LLLayoutStack::Params layout_p; - layout_p.rect = stack_rect; - layout_p.follows.flags = FOLLOWS_ALL; - layout_p.orientation = LLLayoutStack::VERTICAL; - layout_p.mouse_opaque = false; - - LLLayoutStack* stackp = LLUICtrlFactory::create(layout_p, this); - - const S32 NEW_TEXT_NOTICE_HEIGHT = 20; - - LLLayoutPanel::Params panel_p; - panel_p.name = "spacer"; - panel_p.background_visible = false; - panel_p.has_border = false; - panel_p.mouse_opaque = false; - panel_p.min_dim = 30; - panel_p.auto_resize = true; - panel_p.user_resize = false; - - stackp->addPanel(LLUICtrlFactory::create(panel_p), LLLayoutStack::ANIMATE); - - panel_p.name = "new_text_notice_holder"; - LLRect new_text_notice_rect = getLocalRect(); - new_text_notice_rect.mTop = new_text_notice_rect.mBottom + NEW_TEXT_NOTICE_HEIGHT; - panel_p.rect = new_text_notice_rect; - panel_p.background_opaque = true; - panel_p.background_visible = true; - panel_p.visible = false; - panel_p.min_dim = 0; - panel_p.auto_resize = false; - panel_p.user_resize = false; - mMoreChatPanel = LLUICtrlFactory::create(panel_p); - - LLTextBox::Params text_p(p.more_chat_text); - text_p.rect = mMoreChatPanel->getLocalRect(); - text_p.follows.flags = FOLLOWS_ALL; - text_p.name = "more_chat_text"; - mMoreChatText = LLUICtrlFactory::create(text_p, mMoreChatPanel); - mMoreChatText->setClickedCallback(boost::bind(&LLChatHistory::onClickMoreText, this)); - - stackp->addPanel(mMoreChatPanel, LLLayoutStack::ANIMATE); -} - - -/*void LLChatHistory::updateTextRect() -{ - static LLUICachedControl texteditor_border ("UITextEditorBorder", 0); - - LLRect old_text_rect = mVisibleTextRect; - mVisibleTextRect = mScroller->getContentWindowRect(); - mVisibleTextRect.stretch(-texteditor_border); - mVisibleTextRect.mLeft += mLeftTextPad; - mVisibleTextRect.mRight -= mRightTextPad; - if (mVisibleTextRect != old_text_rect) - { - needsReflow(); - } -}*/ - -LLView* LLChatHistory::getSeparator() -{ - LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance()); - return separator; -} - -LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args) -{ - LLChatHistoryHeader* header = LLChatHistoryHeader::createInstance(mMessageHeaderFilename); - if (header) - header->setup(chat, style_params, args); - return header; -} - -void LLChatHistory::onClickMoreText() -{ - mEditor->endOfDoc(); -} - -void LLChatHistory::clear() -{ - mLastFromName.clear(); - mEditor->clear(); - mLastFromID = LLUUID::null; -} - -static LLTrace::BlockTimerStatHandle FTM_APPEND_MESSAGE("Append Chat Message"); - -void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LLStyle::Params& input_append_params) -{ - LL_RECORD_BLOCK_TIME(FTM_APPEND_MESSAGE); - bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean(); - bool square_brackets = false; // square brackets necessary for a system messages - - llassert(mEditor); - if (!mEditor) - return; - - bool from_me = chat.mFromID == gAgent.getID(); - mEditor->setPlainText(use_plain_text_chat_history); - - if (mNotifyAboutUnreadMsg && !mEditor->scrolledToEnd() && !from_me && !chat.mFromName.empty()) - { - mUnreadChatSources.insert(chat.mFromName); - mMoreChatPanel->setVisible(true); - std::string chatters; - for (const std::string& source : mUnreadChatSources) - { - chatters += chatters.size() ? ", " + source : source; - } - LLStringUtil::format_map_t args; - args["SOURCES"] = chatters; - - std::string xml_desc = mUnreadChatSources.size() == 1 ? - "unread_chat_single" : "unread_chat_multiple"; - mMoreChatText->setValue(LLTrans::getString(xml_desc, args)); - S32 height = mMoreChatText->getTextPixelHeight() + 5; - mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height); - } - - LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); - LLColor4 name_color(txt_color); - - LLViewerChat::getChatColor(chat,txt_color); - LLFontGL* fontp = LLViewerChat::getChatFont(); - std::string font_name = LLFontGL::nameFromFont(fontp); - std::string font_size = LLFontGL::sizeFromFont(fontp); - - LLStyle::Params body_message_params; - body_message_params.color(txt_color); - body_message_params.readonly_color(txt_color); - body_message_params.font.name(font_name); - body_message_params.font.size(font_size); - body_message_params.font.style(input_append_params.font.style); - - LLStyle::Params name_params(body_message_params); - name_params.color(name_color); - name_params.readonly_color(name_color); - - std::string prefix = chat.mText.substr(0, 4); - - //IRC styled /me messages. - bool irc_me = prefix == "/me " || prefix == "/me'"; - - // Delimiter after a name in header copy/past and in plain text mode - std::string delimiter = ": "; - std::string shout = LLTrans::getString("shout"); - std::string whisper = LLTrans::getString("whisper"); - if (chat.mChatType == CHAT_TYPE_SHOUT || - chat.mChatType == CHAT_TYPE_WHISPER || - chat.mText.compare(0, shout.length(), shout) == 0 || - chat.mText.compare(0, whisper.length(), whisper) == 0) - { - delimiter = " "; - } - - // Don't add any delimiter after name in irc styled messages - if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC) - { - delimiter = LLStringUtil::null; - body_message_params.font.style = "ITALIC"; - } - - if (chat.mChatType == CHAT_TYPE_WHISPER) - { - body_message_params.font.style = "ITALIC"; - } - else if (chat.mChatType == CHAT_TYPE_SHOUT) - { - body_message_params.font.style = "BOLD"; - } - - bool message_from_log = chat.mChatStyle == CHAT_STYLE_HISTORY; - bool teleport_separator = chat.mSourceType == CHAT_SOURCE_TELEPORT; - // We graying out chat history by graying out messages that contains full date in a time string - if (message_from_log) - { - txt_color = LLColor4::grey; - body_message_params.color(txt_color); - body_message_params.readonly_color(txt_color); - name_params.color(txt_color); - name_params.readonly_color(txt_color); - } - - bool prependNewLineState = mEditor->getText().size() != 0; - - // compact mode: show a timestamp and name - if (use_plain_text_chat_history) - { - square_brackets = chat.mSourceType == CHAT_SOURCE_SYSTEM; - - LLStyle::Params timestamp_style(body_message_params); - - // out of the timestamp - if (args["show_time"].asBoolean() && !teleport_separator) - { - if (!message_from_log) - { - LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); - timestamp_style.color(timestamp_color); - timestamp_style.readonly_color(timestamp_color); - } - mEditor->appendText("[" + chat.mTimeStr + "] ", prependNewLineState, timestamp_style); - prependNewLineState = false; - } - - // out the opening square bracket (if need) - if (square_brackets) - { - mEditor->appendText("[", prependNewLineState, body_message_params); - prependNewLineState = false; - } - - // names showing - if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size()) - { - // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. - if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) - { - // for object IMs, create a secondlife:///app/objectim SLapp - std::string url = LLViewerChat::getSenderSLURL(chat, args); - - // set the link for the object name to be the objectim SLapp - // (don't let object names with hyperlinks override our objectim Url) - LLStyle::Params link_params(body_message_params); - LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - link_params.color = link_color; - link_params.readonly_color = link_color; - link_params.is_link = true; - link_params.link_href = url; - - mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params); - prependNewLineState = false; - } - else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log && chat.mSourceType != CHAT_SOURCE_REGION) - { - LLStyle::Params link_params(body_message_params); - link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); - - // Add link to avatar's inspector and delimiter to message. - mEditor->appendText(std::string(link_params.link_href) + delimiter, - prependNewLineState, link_params); - prependNewLineState = false; - } - else if (teleport_separator) - { - std::string tp_text = LLTrans::getString("teleport_preamble_compact_chat"); - mEditor->appendText(tp_text + " " + chat.mFromName + "", - prependNewLineState, body_message_params); - prependNewLineState = false; - } - else - { - mEditor->appendText("" + chat.mFromName + "" + delimiter, - prependNewLineState, body_message_params); - prependNewLineState = false; - } - } - } - else // showing timestamp and name in the expanded mode - { - prependNewLineState = false; - LLView* view = NULL; - LLInlineViewSegment::Params p; - p.force_newline = true; - p.left_pad = mLeftWidgetPad; - p.right_pad = mRightWidgetPad; - - LLDate new_message_time = LLDate::now(); - if (!teleport_separator - && mLastFromName == chat.mFromName - && mLastFromID == chat.mFromID - && mLastMessageTime.notNull() - && (new_message_time.secondsSinceEpoch() - mLastMessageTime.secondsSinceEpoch()) < 60.0 - && mIsLastMessageFromLog == message_from_log) //distinguish between current and previous chat session's histories - { - view = getSeparator(); - if (!view) - { - // Might be wiser to make this LL_ERRS, getSeparator() should work in case of correct instalation. - LL_WARNS() << "Failed to create separator from " << mMessageSeparatorFilename << ": can't append to history" << LL_ENDL; - return; - } - - p.top_pad = mTopSeparatorPad; - p.bottom_pad = mBottomSeparatorPad; - } - else - { - view = getHeader(chat, name_params, args); - if (!view) - { - LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL; - return; - } - - p.top_pad = mEditor->getLength() ? mTopHeaderPad : 0; - p.bottom_pad = teleport_separator ? mBottomSeparatorPad : mBottomHeaderPad; - } - p.view = view; - - //Prepare the rect for the view - LLRect target_rect = mEditor->getDocumentView()->getRect(); - // squeeze down the widget by subtracting padding off left and right - target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad(); - target_rect.mRight -= mRightWidgetPad; - view->reshape(target_rect.getWidth(), view->getRect().getHeight()); - view->setOrigin(target_rect.mLeft, view->getRect().mBottom); - - std::string widget_associated_text = "\n[" + chat.mTimeStr + "] "; - if (utf8str_trim(chat.mFromName).size() != 0 && chat.mFromName != SYSTEM_FROM) - widget_associated_text += chat.mFromName + delimiter; - - mEditor->appendWidget(p, widget_associated_text, false); - mLastFromName = chat.mFromName; - mLastFromID = chat.mFromID; - mLastMessageTime = new_message_time; - mIsLastMessageFromLog = message_from_log; - } - - // body of the message processing - - // notify processing - if (chat.mNotifId.notNull()) - { - LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId); - if (notification != NULL) - { - bool create_toast = true; - if (notification->getName() == "OfferFriendship") - { - // We don't want multiple friendship offers to appear, this code checks if there are previous offers - // by iterating though all panels. - // Note: it might be better to simply add a "pending offer" flag somewhere - for (auto& panel : LLToastNotifyPanel::instance_snapshot()) - { - LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); - const std::string& notification_name = panel.getNotificationName(); - if (notification_name == "OfferFriendship" - && panel.isControlPanelEnabled() - && imtoastp) - { - create_toast = false; - break; - } - } - } - - if (create_toast) - { - LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( - notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); - - //Prepare the rect for the view - LLRect target_rect = mEditor->getDocumentView()->getRect(); - // squeeze down the widget by subtracting padding off left and right - target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad(); - target_rect.mRight -= mRightWidgetPad; - notify_box->reshape(target_rect.getWidth(), notify_box->getRect().getHeight()); - notify_box->setOrigin(target_rect.mLeft, notify_box->getRect().mBottom); - - LLInlineViewSegment::Params params; - params.view = notify_box; - params.left_pad = mLeftWidgetPad; - params.right_pad = mRightWidgetPad; - mEditor->appendWidget(params, "\n", false); - } - } - } - // usual messages showing - else if (!teleport_separator) - { - std::string message = irc_me ? chat.mText.substr(3) : chat.mText; - - //MESSAGE TEXT PROCESSING - //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010) - if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull()) - { - std::string slurl_about = SLURL_APP_AGENT + chat.mFromID.asString() + SLURL_ABOUT; - if (message.length() > slurl_about.length() && - message.compare(0, slurl_about.length(), slurl_about) == 0) - { - message = message.substr(slurl_about.length(), message.length()-1); - } - } - - if (irc_me && !use_plain_text_chat_history) - { - std::string from_name = chat.mFromName; - LLAvatarName av_name; - if (!chat.mFromID.isNull() && - LLAvatarNameCache::get(chat.mFromID, &av_name) && - !av_name.isDisplayNameDefault()) - { - from_name = av_name.getCompleteName(); - } - message = from_name + message; - } - - if (square_brackets) - { - message += "]"; - } - - mEditor->appendText(message, prependNewLineState, body_message_params); - prependNewLineState = false; - } - - mEditor->blockUndo(); - - // automatically scroll to end when receiving chat from myself - if (from_me) - { - mEditor->setCursorAndScrollToEnd(); - } -} - -void LLChatHistory::draw() -{ - if (mEditor->scrolledToEnd()) - { - mUnreadChatSources.clear(); - mMoreChatPanel->setVisible(false); - } - - LLUICtrl::draw(); -} +/** + * @file llchathistory.cpp + * @brief LLTextEditor base class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llchathistory.h" + +#include + +#include "llavatarnamecache.h" +#include "llinstantmessage.h" + +#include "llimview.h" +#include "llcommandhandler.h" +#include "llpanel.h" +#include "lluictrlfactory.h" +#include "llscrollcontainer.h" +#include "llagent.h" +#include "llagentdata.h" +#include "llavataractions.h" +#include "llavatariconctrl.h" +#include "llcallingcard.h" //for LLAvatarTracker +#include "llgroupactions.h" +#include "llgroupmgr.h" +#include "llspeakers.h" //for LLIMSpeakerMgr +#include "lltrans.h" +#include "llfloaterreg.h" +#include "llfloaterreporter.h" +#include "llfloatersidepanelcontainer.h" +#include "llmutelist.h" +#include "llstylemap.h" +#include "llslurl.h" +#include "lllayoutstack.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "lltoastnotifypanel.h" +#include "lltooltip.h" +#include "llviewerregion.h" +#include "llviewertexteditor.h" +#include "llworld.h" +#include "lluiconstants.h" +#include "llstring.h" +#include "llurlaction.h" +#include "llviewercontrol.h" +#include "llviewermenu.h" +#include "llviewerobjectlist.h" + +static LLDefaultChildRegistry::Register r("chat_history"); + +const static std::string NEW_LINE(rawstr_to_utf8("\n")); + +const static std::string SLURL_APP_AGENT = "secondlife:///app/agent/"; +const static std::string SLURL_ABOUT = "/about"; + +// support for secondlife:///app/objectim/{UUID}/ SLapps +class LLObjectIMHandler : public LLCommandHandler +{ +public: + // requests will be throttled from a non-trusted browser + LLObjectIMHandler() : LLCommandHandler("objectim", UNTRUSTED_THROTTLE) {} + + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) + { + if (params.size() < 1) + { + return false; + } + + LLUUID object_id; + if (!object_id.set(params[0], false)) + { + return false; + } + + LLSD payload; + payload["object_id"] = object_id; + payload["owner_id"] = query_map["owner"]; + payload["name"] = query_map["name"]; + payload["slurl"] = LLWeb::escapeURL(query_map["slurl"]); + payload["group_owned"] = query_map["groupowned"]; + LLFloaterReg::showInstance("inspect_remote_object", payload); + return true; + } +}; +LLObjectIMHandler gObjectIMHandler; + +class LLChatHistoryHeader: public LLPanel +{ +public: + LLChatHistoryHeader() + : LLPanel(), + mInfoCtrl(NULL), + mPopupMenuHandleAvatar(), + mPopupMenuHandleObject(), + mAvatarID(), + mSourceType(CHAT_SOURCE_UNKNOWN), + mFrom(), + mSessionID(), + mCreationTime(time_corrected()), + mMinUserNameWidth(0), + mUserNameFont(NULL), + mUserNameTextBox(NULL), + mTimeBoxTextBox(NULL), + mNeedsTimeBox(true), + mAvatarNameCacheConnection() + {} + + static LLChatHistoryHeader* createInstance(const std::string& file_name) + { + LLChatHistoryHeader* pInstance = new LLChatHistoryHeader; + pInstance->buildFromFile(file_name); + return pInstance; + } + + ~LLChatHistoryHeader() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + auto menu = mPopupMenuHandleAvatar.get(); + if (menu) + { + menu->die(); + mPopupMenuHandleAvatar.markDead(); + } + menu = mPopupMenuHandleObject.get(); + if (menu) + { + menu->die(); + mPopupMenuHandleObject.markDead(); + } + } + + bool handleMouseUp(S32 x, S32 y, MASK mask) + { + return LLPanel::handleMouseUp(x,y,mask); + } + + void onObjectIconContextMenuItemClicked(const LLSD& userdata) + { + std::string level = userdata.asString(); + + if (level == "profile") + { + LLFloaterReg::showInstance("inspect_remote_object", mObjectData); + } + else if (level == "block") + { + LLMuteList::getInstance()->add(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); + + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with("blocked_to_select", getAvatarId())); + } + else if (level == "unblock") + { + LLMuteList::getInstance()->remove(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); + } + else if (level == "map") + { + std::string url = "secondlife://" + mObjectData["slurl"].asString(); + LLUrlAction::showLocationOnMap(url); + } + else if (level == "teleport") + { + std::string url = "secondlife://" + mObjectData["slurl"].asString(); + LLUrlAction::teleportToLocation(url); + } + + } + + bool onObjectIconContextMenuItemVisible(const LLSD& userdata) + { + std::string level = userdata.asString(); + if (level == "is_blocked") + { + return LLMuteList::getInstance()->isMuted(getAvatarId(), mFrom, LLMute::flagTextChat); + } + else if (level == "not_blocked") + { + return !LLMuteList::getInstance()->isMuted(getAvatarId(), mFrom, LLMute::flagTextChat); + } + return false; + } + + void banGroupMember(const LLUUID& participant_uuid) + { + LLUUID group_uuid = mSessionID; + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); + if (!gdatap) + { + // Not a group + return; + } + + gdatap->banMemberById(participant_uuid); + } + + bool canBanInGroup() + { + LLUUID group_uuid = mSessionID; + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); + if (!gdatap) + { + // Not a group + return false; + } + + if (gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) + && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS)) + { + return true; + } + + return false; + } + + bool canBanGroupMember(const LLUUID& participant_uuid) + { + LLUUID group_uuid = mSessionID; + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); + if (!gdatap) + { + // Not a group + return false; + } + + if (gdatap->mPendingBanRequest) + { + return false; + } + + if (gAgentID == getAvatarId()) + { + //Don't ban self + return false; + } + + if (gdatap->isRoleMemberDataComplete()) + { + if (gdatap->mMembers.size()) + { + LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find(participant_uuid); + if (mi != gdatap->mMembers.end()) + { + LLGroupMemberData* member_data = (*mi).second; + // Is the member an owner? + if (member_data && member_data->isInRole(gdatap->mOwnerRole)) + { + return false; + } + + if (gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) + && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS)) + { + return true; + } + } + } + } + + LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + LLSpeaker * speakerp = speaker_mgr->findSpeaker(participant_uuid).get(); + + if (speakerp + && gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) + && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS)) + { + return true; + } + } + + return false; + } + + bool isGroupModerator() + { + LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (!speaker_mgr) + { + LL_WARNS() << "Speaker manager is missing" << LL_ENDL; + return false; + } + + // Is session a group call/chat? + if(gAgent.isInGroup(mSessionID)) + { + LLSpeaker * speakerp = speaker_mgr->findSpeaker(gAgentID).get(); + + // Is agent a moderator? + return speakerp && speakerp->mIsModerator; + } + + return false; + } + + bool canModerate(const std::string& userdata) + { + // only group moderators can perform actions related to this "enable callback" + if (!isGroupModerator() || gAgentID == getAvatarId()) + { + return false; + } + + LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (!speaker_mgr) + { + return false; + } + + LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()).get(); + if (!speakerp) + { + return false; + } + + bool voice_channel = speakerp->isInVoiceChannel(); + + if ("can_moderate_voice" == userdata) + { + return voice_channel; + } + else if ("can_mute" == userdata) + { + return voice_channel && (speakerp->mStatus != LLSpeaker::STATUS_MUTED); + } + else if ("can_unmute" == userdata) + { + return speakerp->mStatus == LLSpeaker::STATUS_MUTED; + } + else if ("can_allow_text_chat" == userdata) + { + return true; + } + + return false; + } + + void onAvatarIconContextMenuItemClicked(const LLSD& userdata) + { + std::string level = userdata.asString(); + + if (level == "profile") + { + LLAvatarActions::showProfile(getAvatarId()); + } + else if (level == "im") + { + LLAvatarActions::startIM(getAvatarId()); + } + else if (level == "teleport") + { + LLAvatarActions::offerTeleport(getAvatarId()); + } + else if (level == "request_teleport") + { + LLAvatarActions::teleportRequest(getAvatarId()); + } + else if (level == "voice_call") + { + LLAvatarActions::startCall(getAvatarId()); + } + else if (level == "chat_history") + { + LLAvatarActions::viewChatHistory(getAvatarId()); + } + else if (level == "add") + { + LLAvatarActions::requestFriendshipDialog(getAvatarId(), mFrom); + } + else if (level == "remove") + { + LLAvatarActions::removeFriendDialog(getAvatarId()); + } + else if (level == "invite_to_group") + { + LLAvatarActions::inviteToGroup(getAvatarId()); + } + else if (level == "zoom_in") + { + handle_zoom_to_object(getAvatarId()); + } + else if (level == "map") + { + LLAvatarActions::showOnMap(getAvatarId()); + } + else if (level == "share") + { + LLAvatarActions::share(getAvatarId()); + } + else if (level == "pay") + { + LLAvatarActions::pay(getAvatarId()); + } + else if (level == "report_abuse") + { + std::string time_string; + if (mTime > 0) // have frame time + { + time_t current_time = time_corrected(); + time_t message_time = current_time - LLFrameTimer::getElapsedSeconds() + mTime; + + time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] [" + + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + + LLSD substitution; + + substitution["datetime"] = (S32)message_time; + LLStringUtil::format(time_string, substitution); + } + else + { + // From history. This might be empty or not full. + // See LLChatLogParser::parse + time_string = getChild("time_box")->getValue().asString(); + + // Just add current date if not full. + // Should be fine since both times are supposed to be stl + if (!time_string.empty() && time_string.size() < 7) + { + time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] " + time_string; + + LLSD substitution; + // To avoid adding today's date to yesterday's timestamp, + // use creation time instead of current time + substitution["datetime"] = (S32)mCreationTime; + LLStringUtil::format(time_string, substitution); + } + } + LLFloaterReporter::showFromChat(mAvatarID, mFrom, time_string, mText); + } + else if(level == "block_unblock") + { + LLAvatarActions::toggleMute(getAvatarId(), LLMute::flagVoiceChat); + } + else if(level == "mute_unmute") + { + LLAvatarActions::toggleMute(getAvatarId(), LLMute::flagTextChat); + } + else if(level == "toggle_allow_text_chat") + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + speaker_mgr->toggleAllowTextChat(getAvatarId()); + } + else if(level == "group_mute") + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->moderateVoiceParticipant(getAvatarId(), false); + } + } + else if(level == "group_unmute") + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->moderateVoiceParticipant(getAvatarId(), true); + } + } + else if(level == "ban_member") + { + banGroupMember(getAvatarId()); + } + } + + bool onAvatarIconContextMenuItemChecked(const LLSD& userdata) + { + std::string level = userdata.asString(); + + if (level == "is_blocked") + { + return LLMuteList::getInstance()->isMuted(getAvatarId(), LLMute::flagVoiceChat); + } + if (level == "is_muted") + { + return LLMuteList::getInstance()->isMuted(getAvatarId(), LLMute::flagTextChat); + } + else if (level == "is_allowed_text_chat") + { + if (gAgent.isInGroup(mSessionID)) + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if(speaker_mgr) + { + const LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()); + if (NULL != speakerp) + { + return !speakerp->mModeratorMutedText; + } + } + } + return false; + } + return false; + } + + bool onAvatarIconContextMenuItemEnabled(const LLSD& userdata) + { + std::string level = userdata.asString(); + + if (level == "can_allow_text_chat" || level == "can_mute" || level == "can_unmute") + { + return canModerate(userdata); + } + else if (level == "report_abuse") + { + return gAgentID != mAvatarID; + } + else if (level == "can_ban_member") + { + return canBanGroupMember(getAvatarId()); + } + return false; + } + + bool onAvatarIconContextMenuItemVisible(const LLSD& userdata) + { + std::string level = userdata.asString(); + + if (level == "show_mute") + { + LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()).get(); + if (speakerp) + { + return speakerp->isInVoiceChannel() && speakerp->mStatus != LLSpeaker::STATUS_MUTED; + } + } + return false; + } + else if (level == "show_unmute") + { + LLSpeakerMgr * speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + LLSpeaker * speakerp = speaker_mgr->findSpeaker(getAvatarId()).get(); + if (speakerp) + { + return speakerp->mStatus == LLSpeaker::STATUS_MUTED; + } + } + return false; + } + return false; + } + + bool postBuild() + { + setDoubleClickCallback(boost::bind(&LLChatHistoryHeader::showInspector, this)); + + setMouseEnterCallback(boost::bind(&LLChatHistoryHeader::showInfoCtrl, this)); + setMouseLeaveCallback(boost::bind(&LLChatHistoryHeader::hideInfoCtrl, this)); + + mUserNameTextBox = getChild("user_name"); + mTimeBoxTextBox = getChild("time_box"); + + mInfoCtrl = LLUICtrlFactory::getInstance()->createFromFile("inspector_info_ctrl.xml", this, LLPanel::child_registry_t::instance()); + if (mInfoCtrl) + { + mInfoCtrl->setCommitCallback(boost::bind(&LLChatHistoryHeader::onClickInfoCtrl, mInfoCtrl)); + mInfoCtrl->setVisible(false); + } + else + { + LL_ERRS() << "Failed to create an interface element due to missing or corrupted file inspector_info_ctrl.xml" << LL_ENDL; + } + + return LLPanel::postBuild(); + } + + bool pointInChild(const std::string& name,S32 x,S32 y) + { + LLUICtrl* child = findChild(name); + if(!child) + return false; + + LLView* parent = child->getParent(); + if(parent!=this) + { + x-=parent->getRect().mLeft; + y-=parent->getRect().mBottom; + } + + S32 local_x = x - child->getRect().mLeft ; + S32 local_y = y - child->getRect().mBottom ; + return child->pointInView(local_x, local_y); + } + + bool handleRightMouseDown(S32 x, S32 y, MASK mask) + { + if(pointInChild("avatar_icon",x,y) || pointInChild("user_name",x,y)) + { + showContextMenu(x,y); + return true; + } + + return LLPanel::handleRightMouseDown(x,y,mask); + } + + void showInspector() + { + if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType && CHAT_SOURCE_REGION != mSourceType) return; + + if (mSourceType == CHAT_SOURCE_OBJECT) + { + LLFloaterReg::showInstance("inspect_remote_object", mObjectData); + } + else if (mSourceType == CHAT_SOURCE_AGENT) + { + LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarID)); + } + //if chat source is system, you may add "else" here to define behaviour. + } + + static void onClickInfoCtrl(LLUICtrl* info_ctrl) + { + if (!info_ctrl) return; + + LLChatHistoryHeader* header = dynamic_cast(info_ctrl->getParent()); + if (!header) return; + + header->showInspector(); + } + + + const LLUUID& getAvatarId () const { return mAvatarID;} + + void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args) + { + mAvatarID = chat.mFromID; + mSessionID = chat.mSessionID; + mSourceType = chat.mSourceType; + + // To be able to report a message, we need a copy of it's text + // and it's easier to store text directly than trying to get + // it from a lltextsegment or chat's mEditor + mText = chat.mText; + mTime = chat.mTime; + + //*TODO overly defensive thing, source type should be maintained out there + if((chat.mFromID.isNull() && chat.mFromName.empty()) || (chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull())) + { + mSourceType = CHAT_SOURCE_SYSTEM; + } + + mUserNameFont = style_params.font(); + if (!mUserNameTextBox) + { + mUserNameTextBox = getChild("user_name"); + mTimeBoxTextBox = getChild("time_box"); + } + LLTextBox* user_name = mUserNameTextBox; + user_name->setReadOnlyColor(style_params.readonly_color()); + user_name->setColor(style_params.color()); + + if (mSourceType == CHAT_SOURCE_TELEPORT + && chat.mChatStyle == CHAT_STYLE_TELEPORT_SEP) + { + mFrom = chat.mFromName; + mNeedsTimeBox = false; + user_name->setValue(mFrom); + updateMinUserNameWidth(); + LLColor4 sep_color = LLUIColorTable::instance().getColor("ChatTeleportSeparatorColor"); + setTransparentColor(sep_color); + mTimeBoxTextBox->setVisible(false); + } + else if (chat.mFromName.empty() + || mSourceType == CHAT_SOURCE_SYSTEM) + { + mFrom = LLTrans::getString("SECOND_LIFE"); + if(!chat.mFromName.empty() && (mFrom != chat.mFromName)) + { + mFrom += " (" + chat.mFromName + ")"; + } + user_name->setValue(mFrom); + updateMinUserNameWidth(); + } + else if (mSourceType == CHAT_SOURCE_AGENT + && !mAvatarID.isNull() + && chat.mChatStyle != CHAT_STYLE_HISTORY) + { + // ...from a normal user, lookup the name and fill in later. + // *NOTE: Do not do this for chat history logs, otherwise the viewer + // will flood the People API with lookup requests on startup + + // Start with blank so sample data from XUI XML doesn't + // flash on the screen + user_name->setValue( LLSD() ); + fetchAvatarName(); + } + else if (chat.mChatStyle == CHAT_STYLE_HISTORY || + mSourceType == CHAT_SOURCE_AGENT) + { + //if it's an avatar name with a username add formatting + S32 username_start = chat.mFromName.rfind(" ("); + S32 username_end = chat.mFromName.rfind(')'); + + if (username_start != std::string::npos && + username_end == (chat.mFromName.length() - 1)) + { + mFrom = chat.mFromName.substr(0, username_start); + user_name->setValue(mFrom); + + if (gSavedSettings.getBOOL("NameTagShowUsernames")) + { + std::string username = chat.mFromName.substr(username_start + 2); + username = username.substr(0, username.length() - 1); + LLStyle::Params style_params_name; + LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); + style_params_name.color(userNameColor); + style_params_name.font.name("SansSerifSmall"); + style_params_name.font.style("NORMAL"); + style_params_name.readonly_color(userNameColor); + user_name->appendText(" - " + username, false, style_params_name); + } + } + else + { + mFrom = chat.mFromName; + user_name->setValue(mFrom); + updateMinUserNameWidth(); + } + } + else + { + // ...from an object, just use name as given + mFrom = chat.mFromName; + user_name->setValue(mFrom); + updateMinUserNameWidth(); + } + + + setTimeField(chat); + + // Set up the icon. + LLAvatarIconCtrl* icon = getChild("avatar_icon"); + + if(mSourceType != CHAT_SOURCE_AGENT || mAvatarID.isNull()) + icon->setDrawTooltip(false); + + switch (mSourceType) + { + case CHAT_SOURCE_AGENT: + icon->setValue(chat.mFromID); + break; + case CHAT_SOURCE_OBJECT: + icon->setValue(LLSD("OBJECT_Icon")); + break; + case CHAT_SOURCE_SYSTEM: + case CHAT_SOURCE_REGION: + icon->setValue(LLSD("SL_Logo")); + break; + case CHAT_SOURCE_TELEPORT: + icon->setValue(LLSD("Command_Destinations_Icon")); + break; + case CHAT_SOURCE_UNKNOWN: + icon->setValue(LLSD("Unknown_Icon")); + } + + // In case the message came from an object, save the object info + // to be able properly show its profile. + if ( chat.mSourceType == CHAT_SOURCE_OBJECT) + { + std::string slurl = args["slurl"].asString(); + if (slurl.empty() && LLWorld::instanceExists()) + { + LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent); + if(region) + { + LLSLURL region_slurl(region->getName(), chat.mPosAgent); + slurl = region_slurl.getLocationString(); + } + } + + LLSD payload; + payload["object_id"] = chat.mFromID; + payload["name"] = chat.mFromName; + payload["owner_id"] = chat.mOwnerID; + payload["slurl"] = LLWeb::escapeURL(slurl); + + mObjectData = payload; + } + } + + /*virtual*/ void draw() + { + LLTextBox* user_name = mUserNameTextBox; //getChild("user_name"); + LLTextBox* time_box = mTimeBoxTextBox; //getChild("time_box"); + + LLRect user_name_rect = user_name->getRect(); + S32 user_name_width = user_name_rect.getWidth(); + S32 time_box_width = time_box->getRect().getWidth(); + + if (mNeedsTimeBox && !time_box->getVisible() && user_name_width > mMinUserNameWidth) + { + user_name_rect.mRight -= time_box_width; + user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight()); + user_name->setRect(user_name_rect); + + time_box->setVisible(true); + } + + LLPanel::draw(); + } + + void updateMinUserNameWidth() + { + if (mUserNameFont) + { + LLTextBox* user_name = getChild("user_name"); + const LLWString& text = user_name->getWText(); + mMinUserNameWidth = mUserNameFont->getWidth(text.c_str()) + PADDING; + } + } + +protected: + static const S32 PADDING = 20; + + void showContextMenu(S32 x,S32 y) + { + if(mSourceType == CHAT_SOURCE_SYSTEM) + showSystemContextMenu(x,y); + if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_AGENT) + showAvatarContextMenu(x,y); + if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_OBJECT) + showObjectContextMenu(x,y); + } + + void showSystemContextMenu(S32 x,S32 y) + { + } + + void showObjectContextMenu(S32 x,S32 y) + { + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleObject.get(); + if (!menu) + { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable; + registrar.add("ObjectIcon.Action", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemClicked, this, _2)); + registrar_enable.add("ObjectIcon.Visible", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemVisible, this, _2)); + + menu = LLUICtrlFactory::getInstance()->createFromFile("menu_object_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (menu) + { + mPopupMenuHandleObject = menu->getHandle(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, x, y); + } + else + { + LL_WARNS() << " Failed to create menu_object_icon.xml" << LL_ENDL; + } + } + else + { + LLMenuGL::showPopup(this, menu, x, y); + } + } + + void showAvatarContextMenu(S32 x,S32 y) + { + LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleAvatar.get(); + if (!menu) + { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable; + registrar.add("AvatarIcon.Action", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemClicked, this, _2)); + registrar_enable.add("AvatarIcon.Check", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemChecked, this, _2)); + registrar_enable.add("AvatarIcon.Enable", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemEnabled, this, _2)); + registrar_enable.add("AvatarIcon.Visible", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemVisible, this, _2)); + + menu = LLUICtrlFactory::getInstance()->createFromFile("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (menu) + { + mPopupMenuHandleAvatar = menu->getHandle(); + } + else + { + LL_WARNS() << " Failed to create menu_avatar_icon.xml" << LL_ENDL; + } + } + + if(menu) + { + bool is_friend = LLAvatarActions::isFriend(mAvatarID); + bool is_group_session = gAgent.isInGroup(mSessionID); + + menu->setItemEnabled("Add Friend", !is_friend); + menu->setItemEnabled("Remove Friend", is_friend); + menu->setItemVisible("Moderator Options Separator", is_group_session && isGroupModerator()); + menu->setItemVisible("Moderator Options", is_group_session && isGroupModerator()); + menu->setItemVisible("Group Ban Separator", is_group_session && canBanInGroup()); + menu->setItemVisible("BanMember", is_group_session && canBanInGroup()); + + if(gAgentID == mAvatarID) + { + menu->setItemEnabled("Add Friend", false); + menu->setItemEnabled("Send IM", false); + menu->setItemEnabled("Remove Friend", false); + menu->setItemEnabled("Offer Teleport",false); + menu->setItemEnabled("Request Teleport",false); + menu->setItemEnabled("Voice Call", false); + menu->setItemEnabled("Chat History", false); + menu->setItemEnabled("Invite Group", false); + menu->setItemEnabled("Zoom In", false); + menu->setItemEnabled("Share", false); + menu->setItemEnabled("Pay", false); + menu->setItemEnabled("Block Unblock", false); + menu->setItemEnabled("Mute Text", false); + } + else + { + LLUUID currentSessionID = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, mAvatarID); + if (mSessionID == currentSessionID) + { + menu->setItemVisible("Send IM", false); + } + menu->setItemEnabled("Offer Teleport", LLAvatarActions::canOfferTeleport(mAvatarID)); + menu->setItemEnabled("Request Teleport", LLAvatarActions::canOfferTeleport(mAvatarID)); + menu->setItemEnabled("Voice Call", LLAvatarActions::canCall()); + + // We should only show 'Zoom in' item in a nearby chat + bool should_show_zoom = !LLIMModel::getInstance()->findIMSession(currentSessionID); + menu->setItemVisible("Zoom In", should_show_zoom && gObjectList.findObject(mAvatarID)); + menu->setItemEnabled("Block Unblock", LLAvatarActions::canBlock(mAvatarID)); + menu->setItemEnabled("Mute Text", LLAvatarActions::canBlock(mAvatarID)); + menu->setItemEnabled("Chat History", LLLogChat::isTranscriptExist(mAvatarID)); + } + + menu->setItemEnabled("Map", (LLAvatarTracker::instance().isBuddyOnline(mAvatarID) && is_agent_mappable(mAvatarID)) || gAgent.isGodlike() ); + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, x, y); + } + } + + void showInfoCtrl() + { + const bool isVisible = !mAvatarID.isNull() && !mFrom.empty() && CHAT_SOURCE_SYSTEM != mSourceType && CHAT_SOURCE_REGION != mSourceType; + if (isVisible) + { + const LLRect sticky_rect = mUserNameTextBox->getRect(); + S32 icon_x = llmin(sticky_rect.mLeft + mUserNameTextBox->getTextBoundingRect().getWidth() + 7, sticky_rect.mRight - 3); + mInfoCtrl->setOrigin(icon_x, sticky_rect.getCenterY() - mInfoCtrl->getRect().getHeight() / 2 ) ; + } + mInfoCtrl->setVisible(isVisible); + } + + void hideInfoCtrl() + { + mInfoCtrl->setVisible(false); + } + +private: + void setTimeField(const LLChat& chat) + { + LLTextBox* time_box = getChild("time_box"); + + LLRect rect_before = time_box->getRect(); + + time_box->setValue(chat.mTimeStr); + + // set necessary textbox width to fit all text + time_box->reshapeToFitText(); + LLRect rect_after = time_box->getRect(); + + // move rect to the left to correct position... + S32 delta_pos_x = rect_before.getWidth() - rect_after.getWidth(); + S32 delta_pos_y = rect_before.getHeight() - rect_after.getHeight(); + time_box->translate(delta_pos_x, delta_pos_y); + + //... & change width of the name control + LLView* user_name = getChild("user_name"); + const LLRect& user_rect = user_name->getRect(); + user_name->reshape(user_rect.getWidth() + delta_pos_x, user_rect.getHeight()); + } + + void fetchAvatarName() + { + if (mAvatarID.notNull()) + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2)); + } + } + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) + { + mAvatarNameCacheConnection.disconnect(); + + mFrom = av_name.getDisplayName(); + + LLTextBox* user_name = getChild("user_name"); + user_name->setValue( LLSD(av_name.getDisplayName() ) ); + user_name->setToolTip( av_name.getUserName() ); + + if (gSavedSettings.getBOOL("NameTagShowUsernames") && + av_name.useDisplayNames() && + !av_name.isDisplayNameDefault()) + { + LLStyle::Params style_params_name; + LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); + style_params_name.color(userNameColor); + style_params_name.font.name("SansSerifSmall"); + style_params_name.font.style("NORMAL"); + style_params_name.readonly_color(userNameColor); + user_name->appendText(" - " + av_name.getUserName(), false, style_params_name); + } + setToolTip( av_name.getUserName() ); + // name might have changed, update width + updateMinUserNameWidth(); + } + +protected: + LLHandle mPopupMenuHandleAvatar; + LLHandle mPopupMenuHandleObject; + + LLUICtrl* mInfoCtrl; + + LLUUID mAvatarID; + LLSD mObjectData; + EChatSourceType mSourceType; + std::string mFrom; + LLUUID mSessionID; + std::string mText; + F64 mTime; // IM's frame time + time_t mCreationTime; // Views's time + + S32 mMinUserNameWidth; + const LLFontGL* mUserNameFont; + LLTextBox* mUserNameTextBox; + LLTextBox* mTimeBoxTextBox; + + bool mNeedsTimeBox; + +private: + boost::signals2::connection mAvatarNameCacheConnection; +}; + +LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) +: LLUICtrl(p), + mMessageHeaderFilename(p.message_header), + mMessageSeparatorFilename(p.message_separator), + mLeftTextPad(p.left_text_pad), + mRightTextPad(p.right_text_pad), + mLeftWidgetPad(p.left_widget_pad), + mRightWidgetPad(p.right_widget_pad), + mTopSeparatorPad(p.top_separator_pad), + mBottomSeparatorPad(p.bottom_separator_pad), + mTopHeaderPad(p.top_header_pad), + mBottomHeaderPad(p.bottom_header_pad), + mIsLastMessageFromLog(false), + mNotifyAboutUnreadMsg(p.notify_unread_msg) +{ + LLTextEditor::Params editor_params(p); + editor_params.rect = getLocalRect(); + editor_params.follows.flags = FOLLOWS_ALL; + editor_params.enabled = false; // read only + editor_params.show_context_menu = "true"; + editor_params.trusted_content = false; + editor_params.text_valign = LLFontGL::VAlign::VCENTER; + editor_params.use_color = true; + mEditor = LLUICtrlFactory::create(editor_params, this); + mEditor->setIsFriendCallback(LLAvatarActions::isFriend); + mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0)); + +} + +LLSD LLChatHistory::getValue() const +{ + return LLSD(mEditor->getText()); +} + +LLChatHistory::~LLChatHistory() +{ + this->clear(); +} + +void LLChatHistory::initFromParams(const LLChatHistory::Params& p) +{ + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + LLRect stack_rect = getLocalRect(); + stack_rect.mRight -= scrollbar_size; + LLLayoutStack::Params layout_p; + layout_p.rect = stack_rect; + layout_p.follows.flags = FOLLOWS_ALL; + layout_p.orientation = LLLayoutStack::VERTICAL; + layout_p.mouse_opaque = false; + + LLLayoutStack* stackp = LLUICtrlFactory::create(layout_p, this); + + const S32 NEW_TEXT_NOTICE_HEIGHT = 20; + + LLLayoutPanel::Params panel_p; + panel_p.name = "spacer"; + panel_p.background_visible = false; + panel_p.has_border = false; + panel_p.mouse_opaque = false; + panel_p.min_dim = 30; + panel_p.auto_resize = true; + panel_p.user_resize = false; + + stackp->addPanel(LLUICtrlFactory::create(panel_p), LLLayoutStack::ANIMATE); + + panel_p.name = "new_text_notice_holder"; + LLRect new_text_notice_rect = getLocalRect(); + new_text_notice_rect.mTop = new_text_notice_rect.mBottom + NEW_TEXT_NOTICE_HEIGHT; + panel_p.rect = new_text_notice_rect; + panel_p.background_opaque = true; + panel_p.background_visible = true; + panel_p.visible = false; + panel_p.min_dim = 0; + panel_p.auto_resize = false; + panel_p.user_resize = false; + mMoreChatPanel = LLUICtrlFactory::create(panel_p); + + LLTextBox::Params text_p(p.more_chat_text); + text_p.rect = mMoreChatPanel->getLocalRect(); + text_p.follows.flags = FOLLOWS_ALL; + text_p.name = "more_chat_text"; + mMoreChatText = LLUICtrlFactory::create(text_p, mMoreChatPanel); + mMoreChatText->setClickedCallback(boost::bind(&LLChatHistory::onClickMoreText, this)); + + stackp->addPanel(mMoreChatPanel, LLLayoutStack::ANIMATE); +} + + +/*void LLChatHistory::updateTextRect() +{ + static LLUICachedControl texteditor_border ("UITextEditorBorder", 0); + + LLRect old_text_rect = mVisibleTextRect; + mVisibleTextRect = mScroller->getContentWindowRect(); + mVisibleTextRect.stretch(-texteditor_border); + mVisibleTextRect.mLeft += mLeftTextPad; + mVisibleTextRect.mRight -= mRightTextPad; + if (mVisibleTextRect != old_text_rect) + { + needsReflow(); + } +}*/ + +LLView* LLChatHistory::getSeparator() +{ + LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance()); + return separator; +} + +LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args) +{ + LLChatHistoryHeader* header = LLChatHistoryHeader::createInstance(mMessageHeaderFilename); + if (header) + header->setup(chat, style_params, args); + return header; +} + +void LLChatHistory::onClickMoreText() +{ + mEditor->endOfDoc(); +} + +void LLChatHistory::clear() +{ + mLastFromName.clear(); + mEditor->clear(); + mLastFromID = LLUUID::null; +} + +static LLTrace::BlockTimerStatHandle FTM_APPEND_MESSAGE("Append Chat Message"); + +void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LLStyle::Params& input_append_params) +{ + LL_RECORD_BLOCK_TIME(FTM_APPEND_MESSAGE); + bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean(); + bool square_brackets = false; // square brackets necessary for a system messages + + llassert(mEditor); + if (!mEditor) + return; + + bool from_me = chat.mFromID == gAgent.getID(); + mEditor->setPlainText(use_plain_text_chat_history); + + if (mNotifyAboutUnreadMsg && !mEditor->scrolledToEnd() && !from_me && !chat.mFromName.empty()) + { + mUnreadChatSources.insert(chat.mFromName); + mMoreChatPanel->setVisible(true); + std::string chatters; + for (const std::string& source : mUnreadChatSources) + { + chatters += chatters.size() ? ", " + source : source; + } + LLStringUtil::format_map_t args; + args["SOURCES"] = chatters; + + std::string xml_desc = mUnreadChatSources.size() == 1 ? + "unread_chat_single" : "unread_chat_multiple"; + mMoreChatText->setValue(LLTrans::getString(xml_desc, args)); + S32 height = mMoreChatText->getTextPixelHeight() + 5; + mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height); + } + + LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); + LLColor4 name_color(txt_color); + + LLViewerChat::getChatColor(chat,txt_color); + LLFontGL* fontp = LLViewerChat::getChatFont(); + std::string font_name = LLFontGL::nameFromFont(fontp); + std::string font_size = LLFontGL::sizeFromFont(fontp); + + LLStyle::Params body_message_params; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + body_message_params.font.name(font_name); + body_message_params.font.size(font_size); + body_message_params.font.style(input_append_params.font.style); + + LLStyle::Params name_params(body_message_params); + name_params.color(name_color); + name_params.readonly_color(name_color); + + std::string prefix = chat.mText.substr(0, 4); + + //IRC styled /me messages. + bool irc_me = prefix == "/me " || prefix == "/me'"; + + // Delimiter after a name in header copy/past and in plain text mode + std::string delimiter = ": "; + std::string shout = LLTrans::getString("shout"); + std::string whisper = LLTrans::getString("whisper"); + if (chat.mChatType == CHAT_TYPE_SHOUT || + chat.mChatType == CHAT_TYPE_WHISPER || + chat.mText.compare(0, shout.length(), shout) == 0 || + chat.mText.compare(0, whisper.length(), whisper) == 0) + { + delimiter = " "; + } + + // Don't add any delimiter after name in irc styled messages + if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC) + { + delimiter = LLStringUtil::null; + body_message_params.font.style = "ITALIC"; + } + + if (chat.mChatType == CHAT_TYPE_WHISPER) + { + body_message_params.font.style = "ITALIC"; + } + else if (chat.mChatType == CHAT_TYPE_SHOUT) + { + body_message_params.font.style = "BOLD"; + } + + bool message_from_log = chat.mChatStyle == CHAT_STYLE_HISTORY; + bool teleport_separator = chat.mSourceType == CHAT_SOURCE_TELEPORT; + // We graying out chat history by graying out messages that contains full date in a time string + if (message_from_log) + { + txt_color = LLColor4::grey; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + name_params.color(txt_color); + name_params.readonly_color(txt_color); + } + + bool prependNewLineState = mEditor->getText().size() != 0; + + // compact mode: show a timestamp and name + if (use_plain_text_chat_history) + { + square_brackets = chat.mSourceType == CHAT_SOURCE_SYSTEM; + + LLStyle::Params timestamp_style(body_message_params); + + // out of the timestamp + if (args["show_time"].asBoolean() && !teleport_separator) + { + if (!message_from_log) + { + LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); + timestamp_style.color(timestamp_color); + timestamp_style.readonly_color(timestamp_color); + } + mEditor->appendText("[" + chat.mTimeStr + "] ", prependNewLineState, timestamp_style); + prependNewLineState = false; + } + + // out the opening square bracket (if need) + if (square_brackets) + { + mEditor->appendText("[", prependNewLineState, body_message_params); + prependNewLineState = false; + } + + // names showing + if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size()) + { + // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. + if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) + { + // for object IMs, create a secondlife:///app/objectim SLapp + std::string url = LLViewerChat::getSenderSLURL(chat, args); + + // set the link for the object name to be the objectim SLapp + // (don't let object names with hyperlinks override our objectim Url) + LLStyle::Params link_params(body_message_params); + LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + link_params.color = link_color; + link_params.readonly_color = link_color; + link_params.is_link = true; + link_params.link_href = url; + + mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params); + prependNewLineState = false; + } + else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log && chat.mSourceType != CHAT_SOURCE_REGION) + { + LLStyle::Params link_params(body_message_params); + link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); + + // Add link to avatar's inspector and delimiter to message. + mEditor->appendText(std::string(link_params.link_href) + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } + else if (teleport_separator) + { + std::string tp_text = LLTrans::getString("teleport_preamble_compact_chat"); + mEditor->appendText(tp_text + " " + chat.mFromName + "", + prependNewLineState, body_message_params); + prependNewLineState = false; + } + else + { + mEditor->appendText("" + chat.mFromName + "" + delimiter, + prependNewLineState, body_message_params); + prependNewLineState = false; + } + } + } + else // showing timestamp and name in the expanded mode + { + prependNewLineState = false; + LLView* view = NULL; + LLInlineViewSegment::Params p; + p.force_newline = true; + p.left_pad = mLeftWidgetPad; + p.right_pad = mRightWidgetPad; + + LLDate new_message_time = LLDate::now(); + if (!teleport_separator + && mLastFromName == chat.mFromName + && mLastFromID == chat.mFromID + && mLastMessageTime.notNull() + && (new_message_time.secondsSinceEpoch() - mLastMessageTime.secondsSinceEpoch()) < 60.0 + && mIsLastMessageFromLog == message_from_log) //distinguish between current and previous chat session's histories + { + view = getSeparator(); + if (!view) + { + // Might be wiser to make this LL_ERRS, getSeparator() should work in case of correct instalation. + LL_WARNS() << "Failed to create separator from " << mMessageSeparatorFilename << ": can't append to history" << LL_ENDL; + return; + } + + p.top_pad = mTopSeparatorPad; + p.bottom_pad = mBottomSeparatorPad; + } + else + { + view = getHeader(chat, name_params, args); + if (!view) + { + LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL; + return; + } + + p.top_pad = mEditor->getLength() ? mTopHeaderPad : 0; + p.bottom_pad = teleport_separator ? mBottomSeparatorPad : mBottomHeaderPad; + } + p.view = view; + + //Prepare the rect for the view + LLRect target_rect = mEditor->getDocumentView()->getRect(); + // squeeze down the widget by subtracting padding off left and right + target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad(); + target_rect.mRight -= mRightWidgetPad; + view->reshape(target_rect.getWidth(), view->getRect().getHeight()); + view->setOrigin(target_rect.mLeft, view->getRect().mBottom); + + std::string widget_associated_text = "\n[" + chat.mTimeStr + "] "; + if (utf8str_trim(chat.mFromName).size() != 0 && chat.mFromName != SYSTEM_FROM) + widget_associated_text += chat.mFromName + delimiter; + + mEditor->appendWidget(p, widget_associated_text, false); + mLastFromName = chat.mFromName; + mLastFromID = chat.mFromID; + mLastMessageTime = new_message_time; + mIsLastMessageFromLog = message_from_log; + } + + // body of the message processing + + // notify processing + if (chat.mNotifId.notNull()) + { + LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId); + if (notification != NULL) + { + bool create_toast = true; + if (notification->getName() == "OfferFriendship") + { + // We don't want multiple friendship offers to appear, this code checks if there are previous offers + // by iterating though all panels. + // Note: it might be better to simply add a "pending offer" flag somewhere + for (auto& panel : LLToastNotifyPanel::instance_snapshot()) + { + LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); + const std::string& notification_name = panel.getNotificationName(); + if (notification_name == "OfferFriendship" + && panel.isControlPanelEnabled() + && imtoastp) + { + create_toast = false; + break; + } + } + } + + if (create_toast) + { + LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( + notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); + + //Prepare the rect for the view + LLRect target_rect = mEditor->getDocumentView()->getRect(); + // squeeze down the widget by subtracting padding off left and right + target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad(); + target_rect.mRight -= mRightWidgetPad; + notify_box->reshape(target_rect.getWidth(), notify_box->getRect().getHeight()); + notify_box->setOrigin(target_rect.mLeft, notify_box->getRect().mBottom); + + LLInlineViewSegment::Params params; + params.view = notify_box; + params.left_pad = mLeftWidgetPad; + params.right_pad = mRightWidgetPad; + mEditor->appendWidget(params, "\n", false); + } + } + } + // usual messages showing + else if (!teleport_separator) + { + std::string message = irc_me ? chat.mText.substr(3) : chat.mText; + + //MESSAGE TEXT PROCESSING + //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010) + if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull()) + { + std::string slurl_about = SLURL_APP_AGENT + chat.mFromID.asString() + SLURL_ABOUT; + if (message.length() > slurl_about.length() && + message.compare(0, slurl_about.length(), slurl_about) == 0) + { + message = message.substr(slurl_about.length(), message.length()-1); + } + } + + if (irc_me && !use_plain_text_chat_history) + { + std::string from_name = chat.mFromName; + LLAvatarName av_name; + if (!chat.mFromID.isNull() && + LLAvatarNameCache::get(chat.mFromID, &av_name) && + !av_name.isDisplayNameDefault()) + { + from_name = av_name.getCompleteName(); + } + message = from_name + message; + } + + if (square_brackets) + { + message += "]"; + } + + mEditor->appendText(message, prependNewLineState, body_message_params); + prependNewLineState = false; + } + + mEditor->blockUndo(); + + // automatically scroll to end when receiving chat from myself + if (from_me) + { + mEditor->setCursorAndScrollToEnd(); + } +} + +void LLChatHistory::draw() +{ + if (mEditor->scrolledToEnd()) + { + mUnreadChatSources.clear(); + mMoreChatPanel->setVisible(false); + } + + LLUICtrl::draw(); +} -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/newview/llchathistory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 3893b21c2a..fc50691ece 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -725,8 +725,8 @@ public: mSourceType == CHAT_SOURCE_AGENT) { //if it's an avatar name with a username add formatting - S32 username_start = chat.mFromName.rfind(" ("); - S32 username_end = chat.mFromName.rfind(')'); + auto username_start = chat.mFromName.rfind(" ("); + auto username_end = chat.mFromName.rfind(')'); if (username_start != std::string::npos && username_end == (chat.mFromName.length() - 1)) -- cgit v1.2.3 From 9fdca96f8bd2211a99fe88e57b70cbecefa20b6d Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 8 Jul 2024 20:27:14 +0200 Subject: Re-enable compiler warnings C4244 and C4396 except for lltracerecording.h and llunittype.h for now --- indra/newview/llchathistory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index fc50691ece..2b02aabc31 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -423,7 +423,7 @@ public: if (mTime > 0) // have frame time { time_t current_time = time_corrected(); - time_t message_time = current_time - LLFrameTimer::getElapsedSeconds() + mTime; + time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime); time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" -- cgit v1.2.3 From c4e921828a41c0e759ee103c6cfdb2cc40c1a581 Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Sat, 3 Aug 2024 10:10:17 -0400 Subject: Optimization and cleanup of various color finds during draw --- indra/newview/llchathistory.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 2b02aabc31..8eba9aee37 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -693,7 +693,7 @@ public: mNeedsTimeBox = false; user_name->setValue(mFrom); updateMinUserNameWidth(); - LLColor4 sep_color = LLUIColorTable::instance().getColor("ChatTeleportSeparatorColor"); + LLUIColor sep_color = LLUIColorTable::instance().getColor("ChatTeleportSeparatorColor"); setTransparentColor(sep_color); mTimeBoxTextBox->setVisible(false); } @@ -739,7 +739,7 @@ public: std::string username = chat.mFromName.substr(username_start + 2); username = username.substr(0, username.length() - 1); LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); + LLUIColor userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); style_params_name.color(userNameColor); style_params_name.font.name("SansSerifSmall"); style_params_name.font.style("NORMAL"); @@ -1037,7 +1037,7 @@ private: !av_name.isDisplayNameDefault()) { LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); + LLUIColor userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); style_params_name.color(userNameColor); style_params_name.font.name("SansSerifSmall"); style_params_name.font.style("NORMAL"); @@ -1239,8 +1239,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height); } - LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); - LLColor4 name_color(txt_color); + LLUIColor txt_color = LLUIColorTable::instance().getColor("White"); + LLUIColor name_color(txt_color); LLViewerChat::getChatColor(chat,txt_color); LLFontGL* fontp = LLViewerChat::getChatFont(); @@ -1317,7 +1317,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL { if (!message_from_log) { - LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); + LLUIColor timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); timestamp_style.color(timestamp_color); timestamp_style.readonly_color(timestamp_color); } @@ -1344,7 +1344,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // set the link for the object name to be the objectim SLapp // (don't let object names with hyperlinks override our objectim Url) LLStyle::Params link_params(body_message_params); - LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + LLUIColor link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); link_params.color = link_color; link_params.readonly_color = link_color; link_params.is_link = true; -- cgit v1.2.3 From 409b9eebe12b864280ead8e3c537a73e40548b97 Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Sun, 4 Aug 2024 20:02:53 -0400 Subject: Fix chat text segments not dynamically updating when colors changed in preferences --- indra/newview/llchathistory.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/newview/llchathistory.cpp') diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 8eba9aee37..a48e22bc73 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1239,10 +1239,11 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height); } + F32 alpha = 1.f; LLUIColor txt_color = LLUIColorTable::instance().getColor("White"); LLUIColor name_color(txt_color); + LLViewerChat::getChatColor(chat, txt_color, alpha); - LLViewerChat::getChatColor(chat,txt_color); LLFontGL* fontp = LLViewerChat::getChatFont(); std::string font_name = LLFontGL::nameFromFont(fontp); std::string font_size = LLFontGL::sizeFromFont(fontp); @@ -1250,6 +1251,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL LLStyle::Params body_message_params; body_message_params.color(txt_color); body_message_params.readonly_color(txt_color); + body_message_params.alpha(alpha); body_message_params.font.name(font_name); body_message_params.font.size(font_size); body_message_params.font.style(input_append_params.font.style); -- cgit v1.2.3