diff options
36 files changed, 805 insertions, 135 deletions
| diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 09124c3013..7b7a3139a4 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -560,6 +560,8 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)  	if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() )  	{ +		ensureSelectedVisible(); +		/*  		LLRect visible_rc = getVisibleContentRect();  		LLRect selected_rc = getLastSelectedItemRect(); @@ -572,7 +574,8 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)  		// In case we are in accordion tab notify parent to show selected rectangle  		LLRect screen_rc;  		localRectToScreen(selected_rc, &screen_rc); -		notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); +		notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/ +  		handled = TRUE;  	} @@ -694,11 +697,30 @@ LLRect LLFlatListView::getSelectedItemsRect()  void LLFlatListView::selectFirstItem	()  {  	selectItemPair(mItemPairs.front(), true); +	ensureSelectedVisible();  }  void LLFlatListView::selectLastItem		()  {  	selectItemPair(mItemPairs.back(), true); +	ensureSelectedVisible(); +} + +void LLFlatListView::ensureSelectedVisible() +{ +	LLRect visible_rc = getVisibleContentRect(); +	LLRect selected_rc = getLastSelectedItemRect(); + +	if ( !visible_rc.contains (selected_rc) ) +	{ +		// But scroll in Items panel coordinates +		scrollToShowRect(selected_rc); +	} + +	// In case we are in accordion tab notify parent to show selected rectangle +	LLRect screen_rc; +	localRectToScreen(selected_rc, &screen_rc); +	notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));  } diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index ba824ff2df..26e84a6fe1 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -359,6 +359,8 @@ protected:  	LLRect getSelectedItemsRect(); +	void   ensureSelectedVisible(); +  private:  	void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;} diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 71b23e1383..5317cf2cd0 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -33,7 +33,7 @@  #include "llviewerprecompiledheaders.h"  #include "llavatarlist.h" -#include "llagent.h" // for comparator +#include "llagentdata.h" // for comparator  // newview  #include "llcallingcard.h" // for LLAvatarTracker @@ -322,7 +322,6 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is  	item->setAvatarId(id, mIgnoreOnlineStatus);  	item->setOnline(mIgnoreOnlineStatus ? true : is_online);  	item->showLastInteractionTime(mShowLastInteractionTime); -	item->setContextMenu(mContextMenu);  	item->childSetVisible("info_btn", false);  	item->setAvatarIconVisible(mShowIcons); @@ -425,11 +424,11 @@ bool LLAvatarItemAgentOnTopComparator::doCompare(const LLAvatarListItem* avatar_  {  	//keep agent on top, if first is agent,   	//then we need to return true to elevate this id, otherwise false. -	if(avatar_item1->getAvatarId() == gAgent.getID()) +	if(avatar_item1->getAvatarId() == gAgentID)  	{  		return true;  	} -	else if (avatar_item2->getAvatarId() == gAgent.getID()) +	else if (avatar_item2->getAvatarId() == gAgentID)  	{  		return false;  	} diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 072eebdf2d..c8544bc3fb 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -43,6 +43,12 @@  #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; +  LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/)  :	LLPanel(),  	mAvatarIcon(NULL), @@ -51,7 +57,6 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/)  	mSpeakingIndicator(NULL),  	mInfoBtn(NULL),  	mProfileBtn(NULL), -	mContextMenu(NULL),  	mOnlineStatus(E_UNKNOWN),  	mShowInfoBtn(true),  	mShowProfileBtn(true) @@ -88,10 +93,15 @@ BOOL  LLAvatarListItem::postBuild()  	// Remember avatar icon width including its padding from the name text box,  	// so that we can hide and show the icon again later. -	mIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; -	mInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; -	mProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; -	mSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight;  +	if (!sStaticInitialized) +	{ +		sIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; +		sInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; +		sProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; +		sSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight; + +		sStaticInitialized = true; +	}  /*  	if(!p.buttons.profile) @@ -173,6 +183,28 @@ void LLAvatarListItem::setHighlight(const std::string& highlight)  	setNameInternal(mAvatarName->getText(), mHighlihtSubstring = highlight);  } +void LLAvatarListItem::setStyle(const LLStyle::Params& new_style) +{ +//	LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle = new_style); + +	// Active group should be bold. +	LLFontDescriptor new_desc(mAvatarName->getDefaultFont()->getFontDesc()); + +	new_desc.setStyle(new_style.font()->getFontDesc().getStyle()); +	// *NOTE dzaporozhan +	// On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font  +	// is predefined as bold (SansSerifSmallBold, for example) +//	new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); +	LLFontGL* new_font = LLFontGL::getFont(new_desc); + +//	 +	mAvatarNameStyle.font = new_font; + +	// *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. +	mAvatarName->setText(mAvatarName->getText(), mAvatarNameStyle/* = new_style*/); +}  void LLAvatarListItem::setAvatarId(const LLUUID& id, bool ignore_status_changes)  {  	if (mAvatarId.notNull()) @@ -214,7 +246,7 @@ void LLAvatarListItem::setShowInfoBtn(bool show)  	if(mShowInfoBtn == show)  		return;  	mShowInfoBtn = show; -	S32 width_delta = show ? - mInfoBtnWidth : mInfoBtnWidth; +	S32 width_delta = show ? - sInfoBtnWidth : sInfoBtnWidth;  	//Translating speaking indicator  	mSpeakingIndicator->translate(width_delta, 0); @@ -228,7 +260,7 @@ void LLAvatarListItem::setShowProfileBtn(bool show)  	if(mShowProfileBtn == show)  			return;  	mShowProfileBtn = show; -	S32 width_delta = show ? - mProfileBtnWidth : mProfileBtnWidth; +	S32 width_delta = show ? - sProfileBtnWidth : sProfileBtnWidth;  	//Translating speaking indicator  	mSpeakingIndicator->translate(width_delta, 0); @@ -242,7 +274,7 @@ void LLAvatarListItem::setSpeakingIndicatorVisible(bool visible)  	if (mSpeakingIndicator->getVisible() == (BOOL)visible)  		return;  	mSpeakingIndicator->setVisible(visible); -	S32 width_delta = visible ? - mSpeakingIndicatorWidth : mSpeakingIndicatorWidth; +	S32 width_delta = visible ? - sSpeakingIndicatorWidth : sSpeakingIndicatorWidth;  	//Reshaping avatar name  	mAvatarName->reshape(mAvatarName->getRect().getWidth() + width_delta, mAvatarName->getRect().getHeight()); @@ -259,7 +291,7 @@ void LLAvatarListItem::setAvatarIconVisible(bool visible)  	// Move the avatar name horizontally by icon size + its distance from the avatar name.  	LLRect name_rect = mAvatarName->getRect(); -	name_rect.mLeft += visible ? mIconWidth : -mIconWidth; +	name_rect.mLeft += visible ? sIconWidth : -sIconWidth;  	mAvatarName->setRect(name_rect);  } @@ -327,10 +359,10 @@ void LLAvatarListItem::onNameCache(const std::string& first_name, const std::str  void LLAvatarListItem::reshapeAvatarName()  {  	S32 width_delta = 0; -	width_delta += mShowProfileBtn ? mProfileBtnWidth : 0; -	width_delta += mSpeakingIndicator->getVisible() ? mSpeakingIndicatorWidth : 0; -	width_delta += mAvatarIcon->getVisible() ? mIconWidth : 0; -	width_delta += mShowInfoBtn ? mInfoBtnWidth : 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(); diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index aa1b7593f5..0e058f75db 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -73,6 +73,7 @@ public:  	void setOnline(bool online);  	void setName(const std::string& name);  	void setHighlight(const std::string& highlight); +	void setStyle(const LLStyle::Params& new_style);  	void setAvatarId(const LLUUID& id, bool ignore_status_changes = false);  	void setLastInteractionTime(U32 secs_since);  	//Show/hide profile/info btn, translating speaker indicator and avatar name coordinates accordingly @@ -91,8 +92,6 @@ public:  	void showInfoBtn(bool show_info_btn) {mInfoBtn->setVisible(show_info_btn); }  	void showLastInteractionTime(bool show); -	void setContextMenu(ContextMenu* menu) { mContextMenu = menu; } -  	/**  	 * This method was added to fix EXT-2364 (Items in group/ad-hoc IM participant list (avatar names) should be reshaped when adding/removing the "(Moderator)" label)  	 * But this is a *HACK. The real reason of it was in incorrect logic while hiding profile/info/speaker buttons @@ -126,7 +125,6 @@ private:  	LLButton* mInfoBtn;  	LLButton* mProfileBtn; -	ContextMenu* mContextMenu;  	LLUUID mAvatarId;  	std::string mHighlihtSubstring; // substring to highlight @@ -135,10 +133,12 @@ private:  	//Speaker indicator and avatar name coords are translated accordingly  	bool mShowInfoBtn;  	bool mShowProfileBtn; -	S32	 mIconWidth; // icon width + padding -	S32  mInfoBtnWidth; //info btn width + padding -	S32  mProfileBtnWidth; //profile btn width + padding -	S32  mSpeakingIndicatorWidth; //speaking indicator width + padding + +	static bool	sStaticInitialized; // this variable is introduced to improve code readability +	static S32	sIconWidth; // icon width + padding +	static S32  sInfoBtnWidth; //info btn width + padding +	static S32  sProfileBtnWidth; //profile btn width + padding +	static S32  sSpeakingIndicatorWidth; //speaking indicator width + padding  };  #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index ab657e0e99..4d5d416907 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -81,9 +81,6 @@ LLBottomTray::LLBottomTray(const LLSD&)  	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("CameraPresets.ChangeView", boost::bind(&LLFloaterCamera::onClickCameraPresets, _2)); -	//managing chiclets for voice calls -	LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLBottomTray::onNewIM, this, _1)); -  	//this is to fix a crash that occurs because LLBottomTray is a singleton  	//and thus is deleted at the end of the viewers lifetime, but to be cleanly  	//destroyed LLBottomTray requires some subsystems that are long gone @@ -153,9 +150,6 @@ void LLBottomTray::sessionAdded(const LLUUID& session_id, const std::string& nam  	if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; -	// For im sessions started as voice call chiclet gets created on the first incoming message -	if (gIMMgr->isVoiceCall(session_id)) return; -  	LLIMChiclet* chiclet = createIMChiclet(session_id);  	if(chiclet)  	{ @@ -197,27 +191,6 @@ void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID&  	}  } -void LLBottomTray::onNewIM(const LLSD& data) -{ -	LLUUID from_id = data["from_id"]; -	if (from_id.isNull() || gAgentID == from_id) return; - -	LLUUID session_id = data["session_id"]; -	if (session_id.isNull()) return; - -	if (!gIMMgr->isVoiceCall(session_id)) return; - -	if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; - -	//first real message, time to create chiclet -	LLIMChiclet* chiclet = createIMChiclet(session_id); -	if(chiclet) -	{ -		chiclet->setIMSessionName(LLIMModel::getInstance()->getName(session_id)); -		chiclet->setOtherParticipantId(LLIMModel::getInstance()->getOtherParticipantID(session_id)); -	} -} -  S32 LLBottomTray::getTotalUnreadIMCount()  {  	return getChicletPanel()->getTotalUnreadIMCount(); diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 5cd3f15746..9be0e5810f 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -75,8 +75,6 @@ public:  	virtual void sessionRemoved(const LLUUID& session_id);  	void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); -	void onNewIM(const LLSD& data); -  	S32 getTotalUnreadIMCount();  	virtual void reshape(S32 width, S32 height, BOOL called_from_parent); diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 20739d2401..fe4f0c5525 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -89,6 +89,7 @@ LLCallFloater::LLCallFloater(const LLSD& key)  , mAgentPanel(NULL)  , mSpeakingIndicator(NULL)  , mIsModeratorMutedVoice(false) +, mInitParticipantsVoiceState(false)  {  	mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);  	LLVoiceClient::getInstance()->addObserver(this); @@ -100,6 +101,8 @@ LLCallFloater::~LLCallFloater()  	delete mPaticipants;  	mPaticipants = NULL; +	mAvatarListRefreshConnection.disconnect(); +  	// Don't use LLVoiceClient::getInstance() here   	// singleton MAY have already been destroyed.  	if(gVoiceClient) @@ -114,6 +117,8 @@ BOOL LLCallFloater::postBuild()  {  	LLDockableFloater::postBuild();  	mAvatarList = getChild<LLAvatarList>("speakers_list"); +	mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this)); +  	childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));  	mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller"); @@ -153,6 +158,10 @@ void LLCallFloater::draw()  		setModeratorMutedVoice(is_moderator_muted);  	} +	// Need to resort the participant list if it's in sort by recent speaker order. +	if (mPaticipants) +		mPaticipants->updateRecentSpeakersOrder(); +  	LLDockableFloater::draw();  } @@ -161,7 +170,7 @@ void LLCallFloater::onChange()  {  	if (NULL == mPaticipants) return; -	mPaticipants->refreshVoiceState(); +	updateParticipantsVoiceState();  } @@ -243,7 +252,7 @@ void LLCallFloater::updateSession()  	childSetVisible("leave_call_btn", !is_local_chat);  	refreshPartisipantList(); -	updateModeratorState(); +	updateAgentModeratorState();  	//show floater for voice calls  	if (!is_local_chat) @@ -280,13 +289,29 @@ void LLCallFloater::refreshPartisipantList()  	if (!non_avatar_caller)  	{ -		mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList); +		mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT);  		if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager)  		{  			mAvatarList->setNoItemsCommentText(getString("no_one_near"));  		} -		mPaticipants->refreshVoiceState();	 + +		// we have to made delayed initialization of voice state of participant list. +		// it will be performed after first LLAvatarList refreshing in the onAvatarListRefreshed(). +		mInitParticipantsVoiceState = true; +	} +} + +void LLCallFloater::onAvatarListRefreshed() +{ +	if (mInitParticipantsVoiceState) +	{ +		initParticipantsVoiceState(); +		mInitParticipantsVoiceState = false; +	} +	else +	{ +		updateParticipantsVoiceState();  	}  } @@ -366,7 +391,7 @@ void LLCallFloater::setModeratorMutedVoice(bool moderator_muted)  	mSpeakingIndicator->setIsMuted(moderator_muted);  } -void LLCallFloater::updateModeratorState() +void LLCallFloater::updateAgentModeratorState()  {  	std::string name;  	gCacheName->getFullName(gAgentID, name); @@ -388,4 +413,213 @@ void LLCallFloater::updateModeratorState()  	}  	mAgentPanel->childSetValue("user_text", name);  } + +void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids) +{ +	// Get a list of participants from VoiceClient +	LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList(); +	if (voice_map) +	{ +		for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin(); +			iter != voice_map->end(); ++iter) +		{ +			LLUUID id = (*iter).second->mAvatarID; +			speakers_uuids.push_back(id); +		} +	} +} + +void LLCallFloater::initParticipantsVoiceState() +{ +	// Set initial status for each participant in the list. +	std::vector<LLPanel*> items; +	mAvatarList->getItems(items); +	std::vector<LLPanel*>::const_iterator +		it = items.begin(), +		it_end = items.end(); + + +	std::vector<LLUUID> speakers_uuids; +	get_voice_participants_uuids(speakers_uuids); + +	for(; it != it_end; ++it) +	{ +		LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it); +		 +		if (!item)	continue; +		 +		LLUUID speaker_id = item->getAvatarId(); + +		std::vector<LLUUID>::const_iterator speaker_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), speaker_id); + +		// If an avatarID assigned to a panel is found in a speakers list +		// obtained from VoiceClient we assign the JOINED status to the owner +		// of this avatarID. +		if (speaker_iter != speakers_uuids.end()) +		{ +			setState(item, STATE_JOINED); +		} +		else +		{ +			LLPointer<LLSpeaker> speakerp = mSpeakerManager->findSpeaker(speaker_id); +			// If someone has already left the call before, we create his +			// avatar row panel with HAS_LEFT status and remove it after +			// the timeout, otherwise we create a panel with INVITED status +			if (speakerp.notNull() && speakerp.get()->mHasLeftCurrentCall) +			{ +				setState(item, STATE_LEFT); +			} +			else +			{ +				setState(item, STATE_INVITED); +			} +		} +	} +} + +void LLCallFloater::updateParticipantsVoiceState() +{ +	std::vector<LLUUID> speakers_list; + +	// Get a list of participants from VoiceClient +	LLVoiceClient::participantMap *map = gVoiceClient->getParticipantList(); +	if (!map) return; + +	for (LLVoiceClient::participantMap::const_iterator iter = map->begin(); +		iter != map->end(); ++iter) +	{ +		LLUUID id = (*iter).second->mAvatarID; +//		if ( id != gAgent.getID() ) +		{ +			speakers_list.push_back(id); +/* +			LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (mAvatarList->getItemByValue(id)); +			if (item) +			{ +				setState(item, STATE_JOINED); +			} +*/ + +		} +	} + +	// Updating the status for each participant. +	std::vector<LLPanel*> items; +	mAvatarList->getItems(items); +	std::vector<LLPanel*>::const_iterator +		it = items.begin(), +		it_end = items.end(); + +	for(; it != it_end; ++it) +	{ +		LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it); +		if (!item) continue; + +		const LLUUID participant_id = item->getAvatarId(); +		bool found = false; + +		std::vector<LLUUID>::iterator speakers_iter = std::find(speakers_list.begin(), speakers_list.end(), participant_id); + +		lldebugs << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << llendl; + +		// If an avatarID assigned to a panel is found in a speakers list +		// obtained from VoiceClient we assign the JOINED status to the owner +		// of this avatarID. +		if (speakers_iter != speakers_list.end()) +		{ +			setState(item, STATE_JOINED); + +			LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id); +			if (speaker.isNull()) +				continue; +			speaker->mHasLeftCurrentCall = FALSE; + +			speakers_list.erase(speakers_iter); +			found = true; +		} + +		// If an avatarID is not found in a speakers list from VoiceClient and +		// a panel with this ID has a JOINED status this means that this person +		// HAS LEFT the call. +		if (!found) +		{ +			if ((getState(participant_id) == STATE_JOINED)) +			{ +				setState(item, STATE_LEFT); + +				LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(item->getAvatarId()); +				if (speaker.isNull()) +					continue; + +				speaker->mHasLeftCurrentCall = TRUE; +			} +			else if ((getState(participant_id) != STATE_LEFT)) +			{ +				setState(item, STATE_INVITED); +			} + +/* +			// If there is already a started timer for the current panel don't do anything. +			bool no_timer_for_current_panel = true; +			if (mTimersMap.size() > 0) +			{ +				timers_map::iterator found_it = mTimersMap.find(participant_id); +				if (found_it != mTimersMap.end()) +				{ +					no_timer_for_current_panel = false; +				} +			} + +			if (no_timer_for_current_panel) +			{ +				// Starting a timer to remove an avatar row panel after timeout +				// *TODO Make the timeout period adjustable +				mTimersMap.insert(timer_pair(participant_id, new LLAvatarRowRemoveTimer(this->getHandle(), 10, participant_id))); +			} +*/ +		} +	} + +} + +void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state) +{ +	setState(item->getAvatarId(), state); + +	LLStyle::Params speaker_style; +	LLFontDescriptor new_desc(speaker_style.font()->getFontDesc()); + +	switch (state) +	{ +	case STATE_INVITED: +//		status_str = "INVITED";			// *TODO: localize +		new_desc.setStyle(LLFontGL::NORMAL); +		break; +	case STATE_JOINED: +//		status_str = "JOINED";			// *TODO: localize +		new_desc.setStyle(LLFontGL::NORMAL); +		break; +	case STATE_LEFT: +		{ +			//		status_str = "HAS LEFT CALL";	// *TODO: localize +			new_desc.setStyle(LLFontGL::ITALIC); + +		} +		break; +	default: +		llwarns << "Unrecognized avatar panel state (" << state << ")" << llendl; +		break; +	} + +	LLFontGL* new_font = LLFontGL::getFont(new_desc); +	speaker_style.font = new_font; +	item->setStyle(speaker_style); + +//	if () +	{ +		// found speaker is in voice, mark him as online +		item->setOnline(STATE_JOINED == state); +	} +} +  //EOF diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index f9c9149085..21fba433c6 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -38,6 +38,7 @@  #include "llvoiceclient.h"  class LLAvatarList; +class LLAvatarListItem;  class LLNonAvatarCaller;  class LLOutputMonitorCtrl;  class LLParticipantList; @@ -81,6 +82,16 @@ private:  		VC_PEER_TO_PEER  	}EVoiceControls; +	typedef enum e_speaker_state +	{ +		STATE_UNKNOWN, +		STATE_INVITED, +		STATE_JOINED, +		STATE_LEFT, +	} ESpeakerState; + +	typedef std::map<LLUUID, ESpeakerState> speaker_state_map_t; +  	void leaveCall();  	/** @@ -95,15 +106,32 @@ private:  	 * Refreshes participant list according to current Voice Channel  	 */  	void refreshPartisipantList(); - +	void onAvatarListRefreshed();  	void updateTitle();  	void initAgentData();  	void setModeratorMutedVoice(bool moderator_muted); -	void updateModeratorState(); +	void updateAgentModeratorState(); + +	void initParticipantsVoiceState(); +	void updateParticipantsVoiceState(); + +	void setState(LLAvatarListItem* item, ESpeakerState state); +	void setState(const LLUUID& speaker_id, ESpeakerState state) +	{ +		lldebugs << "Storing state: " << speaker_id << ", " << state << llendl; +		mSpeakerStateMap[speaker_id] = state; +	} + +	ESpeakerState getState(const LLUUID& speaker_id) +	{ +		lldebugs << "Getting state: " << speaker_id << ", " << mSpeakerStateMap[speaker_id] << llendl; +		return mSpeakerStateMap[speaker_id]; +	}  private: +	speaker_state_map_t mSpeakerStateMap;  	LLSpeakerMgr* mSpeakerManager;  	LLParticipantList* mPaticipants;  	LLAvatarList* mAvatarList; @@ -112,6 +140,11 @@ private:  	LLPanel* mAgentPanel;  	LLOutputMonitorCtrl* mSpeakingIndicator;  	bool mIsModeratorMutedVoice; + +	bool mInitParticipantsVoiceState; + +	boost::signals2::connection mAvatarListRefreshConnection; +  }; diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 21a0381495..17ef1f41a4 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -68,7 +68,8 @@ static const LLRect CHICLET_RECT(0, 25, 25, 0);  static const LLRect CHICLET_ICON_RECT(0, 22, 22, 0);  static const LLRect VOICE_INDICATOR_RECT(50, 25, 70, 0);  static const LLRect COUNTER_RECT(25, 25, 50, 0); -static const S32	OVERLAY_ICON_SHIFT = 2;	// used for shifting of an overlay icon for new massages in a chiclet +static const S32 OVERLAY_ICON_SHIFT = 2;	// used for shifting of an overlay icon for new massages in a chiclet +static const S32 SCROLL_BUTTON_PAD = 5;  // static  const S32 LLChicletPanel::s_scroll_ratio = 10; @@ -140,7 +141,7 @@ private:  LLSysWellChiclet::Params::Params()  : button("button")  , unread_notifications("unread_notifications") -, max_displayed_count("max_displayed_count", 9) +, max_displayed_count("max_displayed_count", 99)  , flash_to_lit_count("flash_to_lit_count", 3)  , flash_period("flash_period", 0.5F)  { @@ -186,9 +187,9 @@ void LLSysWellChiclet::setCounter(S32 counter)  	mButton->setLabel(s_count); -	setNewMessagesState(counter > 0); +	setNewMessagesState(counter > mCounter); -	// we have to flash to 'Lit' state each time new unread message is comming. +	// we have to flash to 'Lit' state each time new unread message is coming.  	if (counter > mCounter)  	{  		mFlashToLitTimer->flash(); @@ -1311,7 +1312,6 @@ bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index)  		chiclet->setChicletSizeChangedCallback(boost::bind(&LLChicletPanel::onChicletSizeChanged, this, _1, index));  		arrange(); -		showScrollButtonsIfNeeded();  		return true;  	} @@ -1322,8 +1322,6 @@ bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index)  void LLChicletPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param)  {  	arrange(); -	trimChiclets(); -	showScrollButtonsIfNeeded();  }  void LLChicletPanel::onChicletClick(LLUICtrl*ctrl,const LLSD¶m) @@ -1340,8 +1338,6 @@ void LLChicletPanel::removeChiclet(chiclet_list_t::iterator it)  	mChicletList.erase(it);  	arrange(); -	trimChiclets(); -	showScrollButtonsIfNeeded();  }  void LLChicletPanel::removeChiclet(S32 index) @@ -1434,8 +1430,6 @@ void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent )  {  	LLPanel::reshape(width,height,called_from_parent); -	static const S32 SCROLL_BUTTON_PAD = 5; -  	//Needed once- to avoid error at first call of reshape() before postBuild()  	if(!mLeftScrollButton||!mRightScrollButton)  		return; @@ -1446,9 +1440,21 @@ void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent )  	scroll_button_rect = mRightScrollButton->getRect();  	mRightScrollButton->setRect(LLRect(width - scroll_button_rect.getWidth(),scroll_button_rect.mTop,  		width, scroll_button_rect.mBottom)); -	mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, -		height, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); +	 + +	bool need_show_scroll = needShowScroll(); +	if(need_show_scroll) +	{ +		mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, +			height, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); +	} +	else +	{ +		mScrollArea->setRect(LLRect(0,height, width, 0)); +	} +	  	mShowControls = width >= mMinWidth; +	  	mScrollArea->setVisible(mShowControls);  	trimChiclets(); @@ -1461,8 +1467,8 @@ void LLChicletPanel::arrange()  	if(mChicletList.empty())  		return; +	//initial arrange of chicklets positions  	S32 chiclet_left = getChiclet(0)->getRect().mLeft; -  	S32 size = getChicletCount();  	for( int n = 0; n < size; ++n)  	{ @@ -1476,6 +1482,24 @@ void LLChicletPanel::arrange()  		chiclet_left += chiclet_width + getChicletPadding();  	} + +	//reset size and pos on mScrollArea +	LLRect rect = getRect(); +	LLRect scroll_button_rect = mLeftScrollButton->getRect(); +	 +	bool need_show_scroll = needShowScroll(); +	if(need_show_scroll) +	{ +		mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, +			rect.getHeight(), rect.getWidth() - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); +	} +	else +	{ +		mScrollArea->setRect(LLRect(0,rect.getHeight(), rect.getWidth(), 0)); +	} +	 +	trimChiclets(); +	showScrollButtonsIfNeeded();  }  void LLChicletPanel::trimChiclets() @@ -1493,6 +1517,17 @@ void LLChicletPanel::trimChiclets()  	}  } +bool LLChicletPanel::needShowScroll() +{ +	if(mChicletList.empty()) +		return false; +	 +	S32 chicklet_width  = (*mChicletList.rbegin())->getRect().mRight - (*mChicletList.begin())->getRect().mLeft; + +	return chicklet_width>getRect().getWidth(); +} + +  void LLChicletPanel::showScrollButtonsIfNeeded()  {  	bool can_scroll_left = canScrollLeft(); diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 259476c2ad..2ab6abfb5b 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -1050,6 +1050,11 @@ protected:  	bool canScrollRight();  	/** +	 * Returns true if we need to show scroll buttons +	 */ +	bool needShowScroll(); + +	/**  	 * Returns true if chiclets can be scrolled left.  	 */  	bool canScrollLeft(); diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index ff75d461df..22658b4d65 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -119,6 +119,36 @@ void LLGroupActions::search()  }  // static +void LLGroupActions::startCall(const LLUUID& group_id) +{ +	// create a new group voice session +	LLGroupData gdata; + +	if (!gAgent.getGroupData(group_id, gdata)) +	{ +		llwarns << "Error getting group data" << llendl; +		return; +	} + +	LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, true); +	if (session_id == LLUUID::null) +	{ +		llwarns << "Error adding session" << llendl; +		return; +	} + +	// start the call +	// *TODO: move this to LLIMMgr? +	LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); +	if (session && session->mSessionInitialized) +		gIMMgr->startCall(session_id); +	else +		gIMMgr->autoStartCallOnStartup(session_id); + +	make_ui_sound("UISndStartIM"); +} + +// static  void LLGroupActions::join(const LLUUID& group_id)  {  	LLGroupMgrGroupData* gdatap =  diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h index 9750b3e3cb..e99df86cd9 100644 --- a/indra/newview/llgroupactions.h +++ b/indra/newview/llgroupactions.h @@ -99,6 +99,11 @@ public:  	static bool isInGroup(const LLUUID& group_id);  	/** +	 * Start a group voice call. +	 */ +	static void startCall(const LLUUID& group_id); + +	/**  	 * Returns true if avatar is in group.  	 *  	 * Note that data about group members is loaded from server. diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index ab9db10f38..3ca459a403 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -37,6 +37,7 @@  // libs  #include "llbutton.h"  #include "lliconctrl.h" +#include "llmenugl.h"  #include "lltextbox.h"  #include "lltrans.h" @@ -46,6 +47,7 @@  #include "llfloaterreg.h"  #include "lltextutil.h"  #include "llviewercontrol.h"	// for gSavedSettings +#include "llviewermenu.h"		// for gMenuHolder  static LLDefaultChildRegistry::Register<LLGroupList> r("group_list");  S32 LLGroupListItem::sIconWidth = 0; @@ -88,11 +90,24 @@ LLGroupList::LLGroupList(const Params& p)  	// Set default sort order.  	setComparator(&GROUP_COMPARATOR); + +	// Set up context menu. +	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; +	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + +	registrar.add("People.Groups.Action",			boost::bind(&LLGroupList::onContextMenuItemClick,	this, _2)); +	enable_registrar.add("People.Groups.Enable",	boost::bind(&LLGroupList::onContextMenuItemEnable,	this, _2)); + +	LLMenuGL* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups.xml", +			gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +	if(context_menu) +		mContextMenuHandle = context_menu->getHandle();  }  LLGroupList::~LLGroupList()  {  	gAgent.removeListener(this); +	LLView::deleteViewByHandle(mContextMenuHandle);  }  // virtual @@ -104,6 +119,22 @@ void LLGroupList::draw()  	LLFlatListView::draw();  } +// virtual +BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ +	BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + +	LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get(); +	if (context_menu) +	{ +		context_menu->buildDrawLabels(); +		context_menu->updateParent(LLMenuGL::sMenuContainer); +		LLMenuGL::showPopup(this, context_menu, x, y); +	} + +	return handled; +} +  void LLGroupList::setNameFilter(const std::string& filter)  {  	if (mNameFilter != filter) @@ -203,6 +234,46 @@ bool LLGroupList::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD&  	return false;  } +bool LLGroupList::onContextMenuItemClick(const LLSD& userdata) +{ +	std::string action = userdata.asString(); +	LLUUID selected_group = getSelectedUUID(); + +	if (action == "view_info") +	{ +		LLGroupActions::show(selected_group); +	} +	else if (action == "chat") +	{ +		LLGroupActions::startIM(selected_group); +	} +	else if (action == "call") +	{ +		LLGroupActions::startCall(selected_group); +	} +	else if (action == "activate") +	{ +		LLGroupActions::activate(selected_group); +	} +	else if (action == "leave") +	{ +		LLGroupActions::leave(selected_group); +	} + +	return true; +} + +bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata) +{ +	LLUUID selected_group_id = getSelectedUUID(); +	bool real_group_selected = selected_group_id.notNull(); // a "real" (not "none") group is selected + +	if (userdata.asString() == "activate") +		return real_group_selected && gAgent.getGroupID() != selected_group_id; + +	return real_group_selected; +} +  /************************************************************************/  /*          LLGroupListItem implementation                              */  /************************************************************************/ diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 33cfe005b9..f7afe0c0b2 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -60,6 +60,7 @@ public:  	virtual ~LLGroupList();  	virtual void draw(); // from LLView +	/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); // from LLView  	void setNameFilter(const std::string& filter);  	void toggleIcons(); @@ -71,6 +72,11 @@ private:  	void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM);  	bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); // called on agent group list changes +	bool onContextMenuItemClick(const LLSD& userdata); +	bool onContextMenuItemEnable(const LLSD& userdata); + +	LLHandle<LLView>	mContextMenuHandle; +  	bool mShowIcons;  	bool mDirty;  	std::string mNameFilter; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index f5362acbfe..8917cc11e1 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1227,15 +1227,15 @@ LLIMMgr::showSessionEventError(  	const std::string& error_string,  	const LLUUID session_id)  { -	const LLFloater* floater = getFloaterBySessionID (session_id); -	if (!floater) return; -  	LLSD args; +	LLStringUtil::format_map_t event_args; + +	event_args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id); +  	args["REASON"] =  		LLTrans::getString(error_string);  	args["EVENT"] = -		LLTrans::getString(event_string); -	args["RECIPIENT"] = floater->getTitle(); +		LLTrans::getString(event_string, event_args);  	LLNotificationsUtil::add(  		"ChatterBoxSessionEventError", diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 22201aecb2..818e7e0db1 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -571,7 +571,7 @@ BOOL LLPanelStandStopFlying::postBuild()  	mStandButton->setVisible(FALSE);  	mStopFlyingButton = getChild<LLButton>("stop_fly_btn"); -	mStopFlyingButton->setCommitCallback(boost::bind(&LLFloaterMove::setFlyingMode, FALSE)); +	//mStopFlyingButton->setCommitCallback(boost::bind(&LLFloaterMove::setFlyingMode, FALSE));  	mStopFlyingButton->setCommitCallback(boost::bind(&LLPanelStandStopFlying::onStopFlyingButtonClick, this));  	mStopFlyingButton->setVisible(FALSE); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 3a1ae5bf46..1a0183a8ba 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -178,6 +178,8 @@ void	LLNearbyChat::addMessage(const LLChat& chat,bool archive)  	if (!chat.mMuted)  	{ +		tmp_chat.mFromName = chat.mFromID != gAgentID ? chat.mFromName : LLTrans::getString("You"); +  		if (chat.mChatStyle == CHAT_STYLE_IRC)  		{  			LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 0dae667e7f..e29320ffc2 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -1557,6 +1557,11 @@ void LLPanelClassifiedEdit::resetControls()  	childSetValue("price_for_listing", MINIMUM_PRICE_FOR_LISTING);  } +bool LLPanelClassifiedEdit::canClose() +{ +	return isValidName(); +} +  void LLPanelClassifiedEdit::sendUpdate()  {  	LLAvatarClassifiedInfo c_data; @@ -1671,6 +1676,12 @@ void LLPanelClassifiedEdit::onChange()  void LLPanelClassifiedEdit::onSaveClick()  { +	if(!isValidName()) +	{ +		notifyInvalidName(); +		return; +	} +  	sendUpdate();  	resetDirty();  } @@ -1681,6 +1692,34 @@ std::string LLPanelClassifiedEdit::getLocationNotice()  	return location_notice;  } +bool LLPanelClassifiedEdit::isValidName() +{ +	std::string name = getClassifiedName(); +	if (name.empty()) +	{ +		return false; +	} +	if (!isalnum(name[0])) +	{ +		return false; +	} + +	return true; +} + +void LLPanelClassifiedEdit::notifyInvalidName() +{ +	std::string name = getClassifiedName(); +	if (name.empty()) +	{ +		LLNotificationsUtil::add("BlankClassifiedName"); +	} +	else if (!isalnum(name[0])) +	{ +		LLNotificationsUtil::add("ClassifiedMustBeAlphanumeric"); +	} +} +  void LLPanelClassifiedEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl)  {  	ctrl->setVisible(TRUE); diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index 8b32495854..10fdf60bbe 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -305,6 +305,8 @@ public:  	bool isNew() { return mIsNew; } +	bool canClose(); +  protected:  	LLPanelClassifiedEdit(); @@ -325,6 +327,10 @@ protected:  	std::string getLocationNotice(); +	bool isValidName(); + +	void notifyInvalidName(); +  	void onSetLocationClick();  	void onChange();  	void onSaveClick(); diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 70e4798079..3f309b3bf5 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -247,6 +247,9 @@ void LLPanelGroupControlPanel::draw()  	//Remove event does not raised until speakerp->mActivityTimer.hasExpired() is false, see LLSpeakerManager::update()  	//so we need update it to raise needed event  	mSpeakerManager->update(true); +	// Need to resort the participant list if it's in sort by recent speaker order. +	if (mParticipantList) +		mParticipantList->updateRecentSpeakersOrder();  	LLPanelChatControlPanel::draw();  } @@ -282,8 +285,9 @@ void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id)  	mGroupID = LLIMModel::getInstance()->getOtherParticipantID(session_id); +	// for group and Ad-hoc chat we need to include agent into list   	if(!mParticipantList) -		mParticipantList = new LLParticipantList(mSpeakerManager, getChild<LLAvatarList>("speakers_list")); +		mParticipantList = new LLParticipantList(mSpeakerManager, getChild<LLAvatarList>("speakers_list"), true,false);  } diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index e134840153..e5846c7318 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -564,6 +564,7 @@ BOOL LLPanelPeople::postBuild()  	buttonSetAction("chat_btn",			boost::bind(&LLPanelPeople::onChatButtonClicked,		this));  	buttonSetAction("im_btn",			boost::bind(&LLPanelPeople::onImButtonClicked,			this));  	buttonSetAction("call_btn",			boost::bind(&LLPanelPeople::onCallButtonClicked,		this)); +	buttonSetAction("group_call_btn",	boost::bind(&LLPanelPeople::onGroupCallButtonClicked,	this));  	buttonSetAction("teleport_btn",		boost::bind(&LLPanelPeople::onTeleportButtonClicked,	this));  	buttonSetAction("share_btn",		boost::bind(&LLPanelPeople::onShareButtonClicked,		this)); @@ -733,6 +734,7 @@ void LLPanelPeople::updateButtons()  	buttonSetVisible("view_profile_btn",	!group_tab_active);  	buttonSetVisible("im_btn",				!group_tab_active);  	buttonSetVisible("call_btn",			!group_tab_active); +	buttonSetVisible("group_call_btn",		group_tab_active);  	buttonSetVisible("teleport_btn",		friends_tab_active);  	buttonSetVisible("share_btn",			nearby_tab_active || friends_tab_active); @@ -781,6 +783,7 @@ void LLPanelPeople::updateButtons()  	bool none_group_selected = item_selected && selected_id.isNull();  	buttonSetEnabled("group_info_btn", !none_group_selected); +	buttonSetEnabled("group_call_btn", !none_group_selected);  	buttonSetEnabled("chat_btn", !none_group_selected);  } @@ -1272,6 +1275,11 @@ void LLPanelPeople::onCallButtonClicked()  	}  } +void LLPanelPeople::onGroupCallButtonClicked() +{ +	LLGroupActions::startCall(getCurrentItemID()); +} +  void LLPanelPeople::onTeleportButtonClicked()  {  	LLAvatarActions::offerTeleport(getCurrentItemID()); diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index f5cdc0935c..0d2bae1baf 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -100,6 +100,7 @@ private:  	void					onChatButtonClicked();  	void					onImButtonClicked();  	void					onCallButtonClicked(); +	void					onGroupCallButtonClicked();  	void					onTeleportButtonClicked();  	void					onShareButtonClicked();  	void					onMoreButtonClicked(); diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 0314642d9e..c1c10e6022 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -184,8 +184,6 @@ bool NearbyMenu::enableContextMenuItem(const LLSD& userdata)  	else if (item == std::string("can_call"))  	{  		bool result = false; -		int size = mUUIDs.size(); -		std::cout << size << std::endl;  		std::vector<LLUUID>::const_iterator  			id = mUUIDs.begin(),  			uuids_end = mUUIDs.end(); diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index 69fa110d11..751705dd57 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -781,6 +781,11 @@ void LLPanelPicks::onPanelPickSave(LLPanel* panel)  void LLPanelPicks::onPanelClassifiedSave(LLPanelClassifiedEdit* panel)  { +	if(!panel->canClose()) +	{ +		return; +	} +  	if(panel->isNew())  	{  		LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), panel->getClassifiedId()); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index afb9892d12..5941487c7d 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -39,7 +39,6 @@  #include "llimview.h"  #include "llparticipantlist.h" -#include "llavatarlist.h"  #include "llspeakers.h"  #include "llviewermenu.h"  #include "llvoiceclient.h" @@ -51,12 +50,13 @@  static const LLAvatarItemAgentOnTopComparator AGENT_ON_TOP_NAME_COMPARATOR; -LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list,  bool use_context_menu/* = true*/): +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list,  bool use_context_menu/* = true*/, +		bool exclude_agent /*= true*/):  	mSpeakerMgr(data_source),  	mAvatarList(avatar_list),  	mSortOrder(E_SORT_BY_NAME)  ,	mParticipantListMenu(NULL) -,	mExcludeAgent(true) +,	mExcludeAgent(exclude_agent)  {  	mSpeakerAddListener = new SpeakerAddListener(*this);  	mSpeakerRemoveListener = new SpeakerRemoveListener(*this); @@ -101,7 +101,6 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av  		}  	}  	// we need to exclude agent id for non group chat -	mExcludeAgent = !gAgent.isInGroup(mSpeakerMgr->getSessionID());  	mAvatarList->setDirty(true);  	sort();  } @@ -204,24 +203,18 @@ void LLParticipantList::setSortOrder(EParticipantSortOrder order)  	}  } -void LLParticipantList::refreshVoiceState() +LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder()  { -	LLSpeakerMgr::speaker_list_t speakers; -	mSpeakerMgr->getSpeakerList(&speakers, TRUE); +	return mSortOrder; +} -	for (LLSpeakerMgr::speaker_list_t::iterator iter = speakers.begin(); -		iter != speakers.end(); ++iter) +void LLParticipantList::updateRecentSpeakersOrder() +{ +	if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder())  	{ -		LLSpeaker* speakerp = (*iter).get(); -		const LLUUID& speaker_id = speakerp->mID; -		LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (mAvatarList->getItemByValue(speaker_id)); -		if ( item ) -		{ -			// if voice is disabled for this speaker show non voice speakers as disabled -			bool is_in_voice = speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE -				&& speakerp->mStatus != LLSpeaker::STATUS_MUTED; -			item->setOnline(!is_in_voice); -		} +		// Resort avatar list +		mAvatarList->setDirty(true); +		sort();  	}  } @@ -312,7 +305,6 @@ void LLParticipantList::sort()  	if ( !mAvatarList )  		return; -	// TODO: Implement more sorting orders after specs updating (EM)  	switch ( mSortOrder ) {  	case E_SORT_BY_NAME :  		// if mExcludeAgent == true , then no need to keep agent on top of the list @@ -326,6 +318,12 @@ void LLParticipantList::sort()  			mAvatarList->sort();  		}  		break; +	case E_SORT_BY_RECENT_SPEAKERS: +		if (mSortByRecentSpeakers.isNull()) +			mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this); +		mAvatarList->setComparator(mSortByRecentSpeakers.get()); +		mAvatarList->sort(); +		break;  	default :  		llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl;  		return; @@ -402,6 +400,7 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu()  	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;  	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; +	registrar.add("ParticipantList.Sort", boost::bind(&LLParticipantList::LLParticipantListMenu::sortParticipantList, this, _2));  	registrar.add("ParticipantList.ToggleAllowTextChat", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleAllowTextChat, this, _2));  	registrar.add("ParticipantList.ToggleMuteText", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteText, this, _2)); @@ -447,6 +446,24 @@ void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const  		LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceUnMuteSelected", false);  		LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceUnMuteOthers", false);  	} + +	// Don't show sort options for P2P chat +	bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); +	LLMenuGL::sMenuContainer->childSetVisible("SortByName", is_sort_visible); +	LLMenuGL::sMenuContainer->childSetVisible("SortByRecentSpeakers", is_sort_visible); +} + +void LLParticipantList::LLParticipantListMenu::sortParticipantList(const LLSD& userdata) +{ +	std::string param = userdata.asString(); +	if ("sort_by_name" == param) +	{ +		mParent.setSortOrder(E_SORT_BY_NAME); +	} +	else if ("sort_by_recent_speakers" == param) +	{ +		mParent.setSortOrder(E_SORT_BY_RECENT_SPEAKERS); +	}  }  void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& userdata) @@ -555,7 +572,7 @@ void LLParticipantList::LLParticipantListMenu::moderateVoiceOtherParticipants(co  bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata)  {  	std::string item = userdata.asString(); -	if (item == "can_mute_text") +	if (item == "can_mute_text" || "can_block" == item)  	{  		return mUUIDs.front() != gAgentID;  	} @@ -616,8 +633,45 @@ bool LLParticipantList::LLParticipantListMenu::checkContextMenuItem(const LLSD&  	{  		return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat);  	} +	else if(item == "is_sorted_by_name") +	{ +		return E_SORT_BY_NAME == mParent.mSortOrder; +	} +	else if(item == "is_sorted_by_recent_speakers") +	{ +		return E_SORT_BY_RECENT_SPEAKERS == mParent.mSortOrder; +	}  	return false;  } +bool LLParticipantList::LLAvatarItemRecentSpeakerComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const +{ +	if (mParent.mSpeakerMgr) +	{ +		LLPointer<LLSpeaker> lhs = mParent.mSpeakerMgr->findSpeaker(avatar_item1->getAvatarId()); +		LLPointer<LLSpeaker> rhs = mParent.mSpeakerMgr->findSpeaker(avatar_item2->getAvatarId()); +		if ( lhs.notNull() && rhs.notNull() ) +		{ +			// Compare by last speaking time +			if( lhs->mLastSpokeTime != rhs->mLastSpokeTime ) +				return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime ); +			else if ( lhs->mSortIndex != rhs->mSortIndex ) +				return ( lhs->mSortIndex < rhs->mSortIndex ); +		} +		else if ( lhs.notNull() ) +		{ +			// True if only avatar_item1 speaker info available +			return true; +		} +		else if ( rhs.notNull() ) +		{ +			// False if only avatar_item2 speaker info available +			return false; +		} +	} +	// By default compare by name. +	return LLAvatarItemNameComparator::doCompare(avatar_item1, avatar_item2); +} +  //EOF diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 72c413d188..c4eb180917 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -34,6 +34,7 @@  #include "llevent.h"  #include "llpanelpeoplemenus.h"  #include "llimview.h" +#include "llavatarlist.h" // for LLAvatarItemRecentSpeakerComparator  class LLSpeakerMgr;  class LLAvatarList; @@ -43,24 +44,25 @@ class LLParticipantList  {  	LOG_CLASS(LLParticipantList);  	public: -		LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu = true); +		LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu = true, bool exclude_agent = true);  		~LLParticipantList();  		void setSpeakingIndicatorsVisible(BOOL visible);  		typedef enum e_participant_sort_oder {  			E_SORT_BY_NAME = 0, +			E_SORT_BY_RECENT_SPEAKERS = 1,  		} EParticipantSortOrder;  		/**  		 * Set and sort Avatarlist by given order  		 */  		void setSortOrder(EParticipantSortOrder order = E_SORT_BY_NAME); +		EParticipantSortOrder getSortOrder();  		/** -		 * Refreshes participants to display ones not in voice as disabled. -		 * TODO: mantipov: probably should be moved into derived class for LLFloaterCall +		 * Refreshes the participant list if it's in sort by recent speaker order.  		 */ -		void refreshVoiceState(); +		void updateRecentSpeakersOrder();  	protected:  		/** @@ -139,6 +141,7 @@ class LLParticipantList  			bool enableContextMenuItem(const LLSD& userdata);  			bool checkContextMenuItem(const LLSD& userdata); +			void sortParticipantList(const LLSD& userdata);  			void toggleAllowTextChat(const LLSD& userdata);  			void toggleMute(const LLSD& userdata, U32 flags);  			void toggleMuteText(const LLSD& userdata); @@ -195,6 +198,21 @@ class LLParticipantList  			void moderateVoiceOtherParticipants(const LLUUID& excluded_avatar_id, bool unmute);  		}; +		/** +		 * Comparator for comparing avatar items by last spoken time +		 */ +		class LLAvatarItemRecentSpeakerComparator : public LLAvatarItemNameComparator, public LLRefCount +		{ +			LOG_CLASS(LLAvatarItemRecentSpeakerComparator); +		  public: +			LLAvatarItemRecentSpeakerComparator(LLParticipantList& parent):mParent(parent){}; +			virtual ~LLAvatarItemRecentSpeakerComparator() {}; +		  protected: +			virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; +		  private: +			LLParticipantList& mParent; +		}; +  	private:  		void onAvatarListDoubleClicked(LLAvatarList* list);  		void onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param); @@ -240,4 +258,6 @@ class LLParticipantList  		boost::signals2::connection mAvatarListDoubleClickConnection;  		boost::signals2::connection mAvatarListRefreshConnection;  		boost::signals2::connection mAvatarListReturnConnection; + +		LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers;  }; diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 3861a96355..91b417c61f 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -630,8 +630,6 @@ void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id)  void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute)  { -	if (gAgentID == avatar_id) return; // do not process myself -  	LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id);  	if (!speakerp) return; diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 26f9824f9c..8c6ea59407 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -352,6 +352,7 @@ LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID&  	}  	// Initialize chiclet. +	mChiclet->setRect(LLRect(5, 28, 30, 3)); // *HACK: workaround for (EXT-3599)  	mChiclet->setChicletSizeChangedCallback(boost::bind(&LLIMWellWindow::RowPanel::onChicletSizeChanged, this, mChiclet, _2));  	mChiclet->enableCounterControl(true);  	mChiclet->setCounter(chicletCounter); @@ -744,9 +745,6 @@ void LLIMWellWindow::sessionAdded(const LLUUID& session_id,  {  	if (mMessageList->getItemByValue(session_id)) return; -	// For im sessions started as voice call chiclet gets created on the first incoming message -	if (gIMMgr->isVoiceCall(session_id)) return; -  	if (!gIMMgr->hasSession(session_id)) return;  	addIMRow(session_id, 0, name, other_participant_id);	 @@ -905,23 +903,6 @@ bool LLIMWellWindow::hasIMRow(const LLUUID& session_id)  	return mMessageList->getItemByValue(session_id);  } -void LLIMWellWindow::onNewIM(const LLSD& data) -{ -	LLUUID from_id = data["from_id"]; -	if (from_id.isNull() || gAgentID == from_id) return; - -	LLUUID session_id = data["session_id"]; -	if (session_id.isNull()) return; - -	if (!gIMMgr->isVoiceCall(session_id)) return; - -	if (hasIMRow(session_id)) return; - -	//first real message, time to create chiclet -	addIMRow(session_id); -} - -  void LLIMWellWindow::closeAll()  {  	// Generate an ignorable alert dialog if there is an active voice IM sesion diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 6cfa25b84d..7030f4b427 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -191,8 +191,6 @@ public:  	/*virtual*/ void sessionRemoved(const LLUUID& session_id);  	/*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); -	void onNewIM(const LLSD& data); -  	void addObjectRow(const LLUUID& object_id, bool new_message = false);  	void removeObjectRow(const LLUUID& object_id); diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp index 7befb87248..347399f239 100644 --- a/indra/newview/lltransientfloatermgr.cpp +++ b/indra/newview/lltransientfloatermgr.cpp @@ -37,6 +37,7 @@  #include "llrootview.h"  #include "llviewerwindow.h"  #include "lldockablefloater.h" +#include "llmenugl.h"  LLTransientFloaterMgr::LLTransientFloaterMgr() @@ -87,6 +88,13 @@ void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y,  	for (controls_set_t::iterator it = mControlsSet.begin(); it  			!= mControlsSet.end(); it++)  	{ +		// don't hide transient floater if any context menu opened +		if (LLMenuGL::sMenuContainer->getVisibleMenu() != NULL) +		{ +			hide = false; +			break; +		} +  		LLView* control_view = *it;  		if (!control_view->getVisible())  		{ diff --git a/indra/newview/skins/default/xui/en/menu_participant_list.xml b/indra/newview/skins/default/xui/en/menu_participant_list.xml index faf33bd1b1..31263fbea8 100644 --- a/indra/newview/skins/default/xui/en/menu_participant_list.xml +++ b/indra/newview/skins/default/xui/en/menu_participant_list.xml @@ -2,7 +2,29 @@  <context_menu   layout="topleft"   name="Participant List Context Menu"> - <menu_item_call +    <menu_item_check +     label="Sort by Name" +     layout="topleft" +     name="SortByName"> +        <menu_item_check.on_click +         function="ParticipantList.Sort" +         parameter="sort_by_name" /> +        <menu_item_check.on_check +         function="ParticipantList.CheckItem" +         parameter="is_sorted_by_name" /> +    </menu_item_check> +    <menu_item_check +     label="Sort by Recent Speakers" +     layout="topleft" +     name="SortByRecentSpeakers"> +        <menu_item_check.on_click +         function="ParticipantList.Sort" +         parameter="sort_by_recent_speakers" /> +        <menu_item_check.on_check +         function="ParticipantList.CheckItem" +         parameter="is_sorted_by_recent_speakers" /> +    </menu_item_check> +    <menu_item_call       label="View Profile"       layout="topleft"       name="View Profile"> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups.xml b/indra/newview/skins/default/xui/en/menu_people_groups.xml new file mode 100644 index 0000000000..afa680139d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_groups.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="false"> +  <menu_item_call +   label="View Info" +   name="View Info"> +    <menu_item_call.on_click +     function="People.Groups.Action" +     parameter="view_info" /> +    <menu_item_call.on_enable +     function="People.Groups.Enable" +     parameter="view_info" /> +  </menu_item_call> +  <menu_item_call +   label="Chat" +   name="Chat"> +    <menu_item_call.on_click +     function="People.Groups.Action" +     parameter="chat" /> +    <menu_item_call.on_enable +     function="People.Groups.Enable" +     parameter="chat" /> +  </menu_item_call> +  <menu_item_call +   label="Call" +   name="Call"> +    <menu_item_call.on_click +     function="People.Groups.Action" +     parameter="call" /> +    <menu_item_call.on_enable +     function="People.Groups.Enable" +     parameter="call" /> +  </menu_item_call> +  <menu_item_separator /> +  <menu_item_call +   label="Activate" +   name="Activate"> +    <menu_item_call.on_click +     function="People.Groups.Action" +     parameter="activate" /> +    <menu_item_call.on_enable +     function="People.Groups.Enable" +     parameter="activate" /> +  </menu_item_call> +  <menu_item_separator /> +  <menu_item_call +   label="Leave" +   name="Leave"> +    <menu_item_call.on_click +     function="People.Groups.Action" +     parameter="leave" /> +    <menu_item_call.on_enable +     function="People.Groups.Enable" +     parameter="leave" /> +  </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 3e2910458f..5ae808581d 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -327,6 +327,7 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly.           min_width="35"           user_resize="false">              <chiclet_im_well +             max_displayed_count="99"               flash_period="0.3"               follows="right"               height="23" @@ -356,7 +357,6 @@ image_pressed_selected  "Lit" + "Selected" - there are new messages and the Well                   image_selected="PushButton_Selected_Press"                   label_color="Black"                   left="0" -                 max_displayed_count="99"                   name="Unread IM messages"                   pad_left="0"                   pad_right="0" diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 8883c27c47..08a10553a8 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -411,5 +411,15 @@ background_visible="true"           name="chat_btn"           tool_tip="Open chat session"           width="110" /> +        <button +         follows="bottom|left" +         top="4" +         left_pad="2" +         height="23" +         label="Group Call" +         layout="topleft" +         name="group_call_btn" +         tool_tip="Call this group" +         width="110" />      </panel>  </panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index a1f2548f81..9aae04ba38 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -2964,12 +2964,26 @@ If you continue to receive this message, contact the [SUPPORT_SITE].    <string name="not_a_mod_error">      You are not a session moderator.    </string> +  <!--Some times string name is getting from the body of server response. +  For ex.: from gIMMgr::showSessionStartError in the LLViewerChatterBoxSessionStartReply::post.  +  In case of the EXT-3562 issue 'muted' is passed into the gIMMgr::showSessionStartError as a string name. +  So, let add string with name="muted" with the same value as "muted_error" --> +  <string name="muted"> +    A group moderator disabled your text chat. +  </string>    <string name="muted_error">      A group moderator disabled your text chat.    </string>    <string name="add_session_event">      Unable to add users to chat session with [RECIPIENT].    </string> +  <!--Some times string name is getting from the body of server response. +  For ex.: from gIMMgr::showSessionStartError in the LLViewerChatterBoxSessionStartReply::post.  +  In case of the EXT-3562 issue 'message' is passed into the gIMMgr::showSessionStartError as a string name. +  So, let add string with name="message" with the same value as "message_session_event" --> +  <string name="message"> +    Unable to send your message to the chat session with [RECIPIENT]. +  </string>      <string name="message_session_event">      Unable to send your message to the chat session with [RECIPIENT].    </string> | 
