/** * @file llavatarlistitem.cpp * @avatar list item source file * * $LicenseInfo:firstyear=2009&license=viewergpl$ * * Copyright (c) 2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llavataractions.h" #include "llavatarlistitem.h" #include "llfloaterreg.h" #include "llagent.h" #include "lloutputmonitorctrl.h" #include "llavatariconctrl.h" #include "lltextutil.h" #include "llbutton.h" bool LLAvatarListItem::sStaticInitialized = false; S32 LLAvatarListItem::sIconWidth = 0; S32 LLAvatarListItem::sInfoBtnWidth = 0; S32 LLAvatarListItem::sProfileBtnWidth = 0; S32 LLAvatarListItem::sSpeakingIndicatorWidth = 0; S32 LLAvatarListItem::sLeftPadding = 0; S32 LLAvatarListItem::sRightNamePadding = 0; S32 LLAvatarListItem::sChildrenWidths[LLAvatarListItem::ALIC_COUNT]; LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) : LLPanel(), mAvatarIcon(NULL), mAvatarName(NULL), mLastInteractionTime(NULL), mSpeakingIndicator(NULL), mInfoBtn(NULL), mProfileBtn(NULL), mOnlineStatus(E_UNKNOWN), mShowInfoBtn(true), mShowProfileBtn(true) { if (not_from_ui_factory) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); } // *NOTE: mantipov: do not use any member here. They can be uninitialized here in case instance // is created from the UICtrlFactory } LLAvatarListItem::~LLAvatarListItem() { if (mAvatarId.notNull()) LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarId, this); } BOOL LLAvatarListItem::postBuild() { mAvatarIcon = getChild("avatar_icon"); mAvatarName = getChild("avatar_name"); mLastInteractionTime = getChild("last_interaction"); mSpeakingIndicator = getChild("speaking_indicator"); mInfoBtn = getChild("info_btn"); mProfileBtn = getChild("profile_btn"); mInfoBtn->setVisible(false); mInfoBtn->setClickedCallback(boost::bind(&LLAvatarListItem::onInfoBtnClick, this)); mProfileBtn->setVisible(false); mProfileBtn->setClickedCallback(boost::bind(&LLAvatarListItem::onProfileBtnClick, this)); // Remember avatar icon width including its padding from the name text box, // so that we can hide and show the icon again later. if (!sStaticInitialized) { initChildrenWidths(this); sStaticInitialized = true; } /* if(!p.buttons.profile) { delete mProfile; mProfile = NULL; LLRect rect; rect.setLeftTopAndSize(mName->getRect().mLeft, mName->getRect().mTop, mName->getRect().getWidth() + 30, mName->getRect().getHeight()); mName->setRect(rect); if(mLocator) { rect.setLeftTopAndSize(mLocator->getRect().mLeft + 30, mLocator->getRect().mTop, mLocator->getRect().getWidth(), mLocator->getRect().getHeight()); mLocator->setRect(rect); } if(mInfo) { rect.setLeftTopAndSize(mInfo->getRect().mLeft + 30, mInfo->getRect().mTop, mInfo->getRect().getWidth(), mInfo->getRect().getHeight()); mInfo->setRect(rect); } } */ return TRUE; } void LLAvatarListItem::onMouseEnter(S32 x, S32 y, MASK mask) { childSetVisible("hovered_icon", true); mInfoBtn->setVisible(mShowInfoBtn); mProfileBtn->setVisible(mShowProfileBtn); LLPanel::onMouseEnter(x, y, mask); updateChildren(); } void LLAvatarListItem::onMouseLeave(S32 x, S32 y, MASK mask) { childSetVisible("hovered_icon", false); mInfoBtn->setVisible(false); mProfileBtn->setVisible(false); LLPanel::onMouseLeave(x, y, mask); updateChildren(); } // virtual, called by LLAvatarTracker void LLAvatarListItem::changed(U32 mask) { // no need to check mAvatarId for null in this case setOnline(LLAvatarTracker::instance().isBuddyOnline(mAvatarId)); } void LLAvatarListItem::setOnline(bool online) { // *FIX: setName() overrides font style set by setOnline(). Not an issue ATM. if (mOnlineStatus != E_UNKNOWN && (bool) mOnlineStatus == online) return; mOnlineStatus = (EOnlineStatus) online; // Change avatar name font style depending on the new online status. setState(online ? IS_ONLINE : IS_OFFLINE); } void LLAvatarListItem::setName(const std::string& name) { setNameInternal(name, mHighlihtSubstring); } void LLAvatarListItem::setHighlight(const std::string& highlight) { setNameInternal(mAvatarName->getText(), mHighlihtSubstring = highlight); } void LLAvatarListItem::setState(EItemState item_style) { item_style_map_t& item_styles_params_map = getItemStylesParams(); mAvatarNameStyle = item_styles_params_map[item_style]; // *NOTE: You cannot set the style on a text box anymore, you must // rebuild the text. This will cause problems if the text contains // hyperlinks, as their styles will be wrong. setNameInternal(mAvatarName->getText(), mHighlihtSubstring); icon_color_map_t& item_icon_color_map = getItemIconColorMap(); mAvatarIcon->setColor(item_icon_color_map[item_style]); } void LLAvatarListItem::setAvatarId(const LLUUID& id, bool ignore_status_changes) { if (mAvatarId.notNull()) LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarId, this); mAvatarId = id; mAvatarIcon->setValue(id); mSpeakingIndicator->setSpeakerId(id); // We'll be notified on avatar online status changes if (!ignore_status_changes && mAvatarId.notNull()) LLAvatarTracker::instance().addParticularFriendObserver(mAvatarId, this); // Set avatar name. gCacheName->get(id, FALSE, boost::bind(&LLAvatarListItem::onNameCache, this, _2, _3)); } void LLAvatarListItem::showLastInteractionTime(bool show) { if (show) return; mLastInteractionTime->setVisible(false); updateChildren(); } void LLAvatarListItem::setLastInteractionTime(U32 secs_since) { mLastInteractionTime->setValue(formatSeconds(secs_since)); } void LLAvatarListItem::setShowInfoBtn(bool show) { // Already done? Then do nothing. if(mShowInfoBtn == show) return; mShowInfoBtn = show; } void LLAvatarListItem::setShowProfileBtn(bool show) { // Already done? Then do nothing. if(mShowProfileBtn == show) return; mShowProfileBtn = show; } void LLAvatarListItem::setSpeakingIndicatorVisible(bool visible) { // Already done? Then do nothing. if (mSpeakingIndicator->getVisible() == (BOOL)visible) return; mSpeakingIndicator->setVisible(visible); updateChildren(); } void LLAvatarListItem::setAvatarIconVisible(bool visible) { // Already done? Then do nothing. if (mAvatarIcon->getVisible() == (BOOL)visible) return; // Show/hide avatar icon. mAvatarIcon->setVisible(visible); updateChildren(); } void LLAvatarListItem::onInfoBtnClick() { LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarId)); /* TODO fix positioning of inspector localPointToScreen(mXPos, mYPos, &mXPos, &mYPos); LLRect rect; // *TODO Vadim: rewrite this. "+= -" looks weird. S32 delta = mYPos - inspector->getRect().getHeight(); if(delta < 0) { mYPos += -delta; } rect.setLeftTopAndSize(mXPos, mYPos, inspector->getRect().getWidth(), inspector->getRect().getHeight()); inspector->setRect(rect); inspector->setFrontmost(true); inspector->setVisible(true); */ } void LLAvatarListItem::onProfileBtnClick() { LLAvatarActions::showProfile(mAvatarId); } void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; if (!value.has("selected")) return; childSetVisible("selected_icon", value["selected"]); } const LLUUID& LLAvatarListItem::getAvatarId() const { return mAvatarId; } const std::string LLAvatarListItem::getAvatarName() const { return mAvatarName->getValue(); } //== PRIVATE SECITON ========================================================== void LLAvatarListItem::setNameInternal(const std::string& name, const std::string& highlight) { LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle, name, highlight); mAvatarName->setToolTip(name); } void LLAvatarListItem::onNameCache(const std::string& first_name, const std::string& last_name) { std::string name = first_name + " " + last_name; setName(name); } void LLAvatarListItem::reshapeAvatarName() { /* S32 width_delta = 0; width_delta += mShowProfileBtn ? sProfileBtnWidth : 0; width_delta += mSpeakingIndicator->getVisible() ? sSpeakingIndicatorWidth : 0; width_delta += mAvatarIcon->getVisible() ? sIconWidth : 0; width_delta += mShowInfoBtn ? sInfoBtnWidth : 0; width_delta += mLastInteractionTime->getVisible() ? mLastInteractionTime->getRect().getWidth() : 0; S32 height = mAvatarName->getRect().getHeight(); S32 width = getRect().getWidth() - width_delta; mAvatarName->reshape(width, height); */ } // Convert given number of seconds to a string like "23 minutes", "15 hours" or "3 years", // taking i18n into account. The format string to use is taken from the panel XML. std::string LLAvatarListItem::formatSeconds(U32 secs) { static const U32 LL_ALI_MIN = 60; static const U32 LL_ALI_HOUR = LL_ALI_MIN * 60; static const U32 LL_ALI_DAY = LL_ALI_HOUR * 24; static const U32 LL_ALI_WEEK = LL_ALI_DAY * 7; static const U32 LL_ALI_MONTH = LL_ALI_DAY * 30; static const U32 LL_ALI_YEAR = LL_ALI_DAY * 365; std::string fmt; U32 count = 0; if (secs >= LL_ALI_YEAR) { fmt = "FormatYears"; count = secs / LL_ALI_YEAR; } else if (secs >= LL_ALI_MONTH) { fmt = "FormatMonths"; count = secs / LL_ALI_MONTH; } else if (secs >= LL_ALI_WEEK) { fmt = "FormatWeeks"; count = secs / LL_ALI_WEEK; } else if (secs >= LL_ALI_DAY) { fmt = "FormatDays"; count = secs / LL_ALI_DAY; } else if (secs >= LL_ALI_HOUR) { fmt = "FormatHours"; count = secs / LL_ALI_HOUR; } else if (secs >= LL_ALI_MIN) { fmt = "FormatMinutes"; count = secs / LL_ALI_MIN; } else { fmt = "FormatSeconds"; count = secs; } LLStringUtil::format_map_t args; args["[COUNT]"] = llformat("%u", count); return getString(fmt, args); } // static LLAvatarListItem::item_style_map_t& LLAvatarListItem::getItemStylesParams() { static item_style_map_t item_styles_params_map; if (!item_styles_params_map.empty()) return item_styles_params_map; LLPanel::Params params = LLUICtrlFactory::getDefaultParams(); LLPanel* params_panel = LLUICtrlFactory::create(params); BOOL sucsess = LLUICtrlFactory::instance().buildPanel(params_panel, "panel_avatar_list_item_params.xml"); if (sucsess) { item_styles_params_map.insert( std::make_pair(IS_DEFAULT, params_panel->getChild("default_style")->getDefaultStyle())); item_styles_params_map.insert( std::make_pair(IS_VOICE_INVITED, params_panel->getChild("voice_call_invited_style")->getDefaultStyle())); item_styles_params_map.insert( std::make_pair(IS_VOICE_JOINED, params_panel->getChild("voice_call_joined_style")->getDefaultStyle())); item_styles_params_map.insert( std::make_pair(IS_VOICE_LEFT, params_panel->getChild("voice_call_left_style")->getDefaultStyle())); item_styles_params_map.insert( std::make_pair(IS_ONLINE, params_panel->getChild("online_style")->getDefaultStyle())); item_styles_params_map.insert( std::make_pair(IS_OFFLINE, params_panel->getChild("offline_style")->getDefaultStyle())); } else { item_styles_params_map.insert(std::make_pair(IS_DEFAULT, LLStyle::Params())); item_styles_params_map.insert(std::make_pair(IS_VOICE_INVITED, LLStyle::Params())); item_styles_params_map.insert(std::make_pair(IS_VOICE_JOINED, LLStyle::Params())); item_styles_params_map.insert(std::make_pair(IS_VOICE_LEFT, LLStyle::Params())); item_styles_params_map.insert(std::make_pair(IS_ONLINE, LLStyle::Params())); item_styles_params_map.insert(std::make_pair(IS_OFFLINE, LLStyle::Params())); } if (params_panel) params_panel->die(); return item_styles_params_map; } // static LLAvatarListItem::icon_color_map_t& LLAvatarListItem::getItemIconColorMap() { static icon_color_map_t item_icon_color_map; if (!item_icon_color_map.empty()) return item_icon_color_map; item_icon_color_map.insert( std::make_pair(IS_DEFAULT, LLUIColorTable::instance().getColor("AvatarListItemIconDefaultColor", LLColor4::white))); item_icon_color_map.insert( std::make_pair(IS_VOICE_INVITED, LLUIColorTable::instance().getColor("AvatarListItemIconVoiceInvitedColor", LLColor4::white))); item_icon_color_map.insert( std::make_pair(IS_VOICE_JOINED, LLUIColorTable::instance().getColor("AvatarListItemIconVoiceJoinedColor", LLColor4::white))); item_icon_color_map.insert( std::make_pair(IS_VOICE_LEFT, LLUIColorTable::instance().getColor("AvatarListItemIconVoiceLeftColor", LLColor4::white))); item_icon_color_map.insert( std::make_pair(IS_ONLINE, LLUIColorTable::instance().getColor("AvatarListItemIconOnlineColor", LLColor4::white))); item_icon_color_map.insert( std::make_pair(IS_OFFLINE, LLUIColorTable::instance().getColor("AvatarListItemIconOfflineColor", LLColor4::white))); return item_icon_color_map; } // static void LLAvatarListItem::initChildrenWidths(LLAvatarListItem* avatar_item) { //profile btn width + padding S32 profile_btn_width = avatar_item->getRect().getWidth() - avatar_item->mProfileBtn->getRect().mLeft; //info btn width + padding S32 info_btn_width = avatar_item->mProfileBtn->getRect().mLeft - avatar_item->mInfoBtn->getRect().mLeft; //speaking indicator width + padding S32 speaking_indicator_width = avatar_item->mInfoBtn->getRect().mLeft - avatar_item->mSpeakingIndicator->getRect().mLeft; // last interaction time textbox width + padding S32 last_interaction_time_width = avatar_item->mSpeakingIndicator->getRect().mLeft - avatar_item->mLastInteractionTime->getRect().mLeft; // icon width + padding S32 icon_width = avatar_item->mAvatarName->getRect().mLeft - avatar_item->mAvatarIcon->getRect().mLeft; sLeftPadding = avatar_item->mAvatarIcon->getRect().mLeft; sRightNamePadding = avatar_item->mLastInteractionTime->getRect().mLeft - avatar_item->mAvatarName->getRect().mRight; S32 index = ALIC_COUNT; sChildrenWidths[--index] = icon_width; sChildrenWidths[--index] = 0; // for avatar name we don't need its width, it will be calculated as "left available space" sChildrenWidths[--index] = last_interaction_time_width; sChildrenWidths[--index] = speaking_indicator_width; sChildrenWidths[--index] = info_btn_width; sChildrenWidths[--index] = profile_btn_width; } void LLAvatarListItem::updateChildren() { LL_DEBUGS("AvatarItemReshape") << LL_ENDL; LL_DEBUGS("AvatarItemReshape") << "Updating for: " << getAvatarName() << LL_ENDL; S32 name_new_width = getRect().getWidth(); S32 ctrl_new_left = name_new_width; S32 name_new_left = sLeftPadding; // iterate through all children and set them into correct position depend on each child visibility // assume that child indexes are in back order: the first in Enum is the last (right) in the item // iterate & set child views starting from right to left for (S32 i = 0; i < ALIC_COUNT; ++i) { // skip "name" textbox, it will be processed out of loop later if (ALIC_NAME == i) continue; LLView* control = getItemChildView((EAvatarListItemChildIndex)i); LL_DEBUGS("AvatarItemReshape") << "Processing control: " << control->getName() << LL_ENDL; // skip invisible views if (!control->getVisible()) continue; S32 ctrl_width = sChildrenWidths[i]; // including space between current & left controls // decrease available for name_new_width -= ctrl_width; LL_DEBUGS("AvatarItemReshape") << "width: " << ctrl_width << ", name_new_width: " << name_new_width << LL_ENDL; LLRect control_rect = control->getRect(); LL_DEBUGS("AvatarItemReshape") << "rect before: " << control_rect << LL_ENDL; if (ALIC_ICON == i) { // assume that this is the last iteration, // so it is not necessary to save "ctrl_new_left" value calculated on previous iterations ctrl_new_left = sLeftPadding;//control_rect.mLeft; name_new_left = ctrl_new_left + ctrl_width; } else { ctrl_new_left -= ctrl_width; } LL_DEBUGS("AvatarItemReshape") << "ctrl_new_left: " << ctrl_new_left << LL_ENDL; control_rect.setLeftTopAndSize( ctrl_new_left, control_rect.mTop, control_rect.getWidth(), control_rect.getHeight()); LL_DEBUGS("AvatarItemReshape") << "rect after: " << control_rect << LL_ENDL; control->setShape(control_rect); } // set size and position of the "name" child LLView* name_view = getItemChildView(ALIC_NAME); LLRect name_view_rect = name_view->getRect(); LL_DEBUGS("AvatarItemReshape") << "name rect before: " << name_view_rect << LL_ENDL; // apply paddings name_new_width -= sLeftPadding; name_new_width -= sRightNamePadding; name_view_rect.setLeftTopAndSize( name_new_left, name_view_rect.mTop, name_new_width, name_view_rect.getHeight()); name_view->setShape(name_view_rect); LL_DEBUGS("AvatarItemReshape") << "name rect after: " << name_view_rect << LL_ENDL; } LLView* LLAvatarListItem::getItemChildView(EAvatarListItemChildIndex child_view_index) { LLView* child_view = mAvatarName; if (child_view_index < 0 || ALIC_COUNT <= child_view_index) { LL_WARNS("AvatarItemReshape") << "Child view index is out of range: " << child_view_index << LL_ENDL; return child_view; } switch (child_view_index) { case ALIC_ICON: child_view = mAvatarIcon; break; case ALIC_NAME: child_view = mAvatarName; break; case ALIC_INTERACTION_TIME: child_view = mLastInteractionTime; break; case ALIC_SPEAKER_INDICATOR: child_view = mSpeakingIndicator; break; case ALIC_INFO_BUTTON: child_view = mInfoBtn; break; case ALIC_PROFILE_BUTTON: child_view = mProfileBtn; break; default: LL_WARNS("AvatarItemReshape") << "Unexpected child view index is passed: " << child_view_index << LL_ENDL; } return child_view; } // EOF