diff options
| author | Tofu Linden <tofu.linden@lindenlab.com> | 2010-01-22 10:51:58 -0800 | 
|---|---|---|
| committer | Tofu Linden <tofu.linden@lindenlab.com> | 2010-01-22 10:51:58 -0800 | 
| commit | 3889d4329962cc497bccbc8bbe9e6678ab3c9e6c (patch) | |
| tree | da02c07350e9c4def5013376dbbc382a3e49d57e /indra/newview | |
| parent | fa891c062ef66410123c66de1ef67b7dcef327db (diff) | |
| parent | 09e9cdeb2e7e979ea1d4898fa7c2d40e8ebc52b2 (diff) | |
PE merge.
Diffstat (limited to 'indra/newview')
48 files changed, 853 insertions, 369 deletions
| diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a4fc095727..c29a3a0035 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10070,6 +10070,18 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>SpeakerParticipantRemoveDelay</key> +    <map> +      <key>Comment</key> +      <string>Timeout to remove participants who is not in channel before removed from list of active speakers (text/voice chat)</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>F32</string> +      <key>Value</key> +      <real>10.0</real> +    </map> +      <key>UseStartScreen</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index f346a4b8c2..d4c8adadc6 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -93,22 +93,6 @@ static void* create_non_avatar_caller(void*)  	return new LLNonAvatarCaller;  } -LLCallFloater::LLAvatarListItemRemoveTimer::LLAvatarListItemRemoveTimer(callback_t remove_cb, F32 period, const LLUUID& speaker_id) -: LLEventTimer(period) -, mRemoveCallback(remove_cb) -, mSpeakerId(speaker_id) -{ -} - -BOOL LLCallFloater::LLAvatarListItemRemoveTimer::tick() -{ -	if (mRemoveCallback) -	{ -		mRemoveCallback(mSpeakerId); -	} -	return TRUE; -} -  LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL;  LLCallFloater::LLCallFloater(const LLSD& key) @@ -122,10 +106,9 @@ LLCallFloater::LLCallFloater(const LLSD& key)  , mSpeakingIndicator(NULL)  , mIsModeratorMutedVoice(false)  , mInitParticipantsVoiceState(false) -, mVoiceLeftRemoveDelay(10)  {  	static LLUICachedControl<S32> voice_left_remove_delay ("VoiceParticipantLeftRemoveDelay", 10); -	mVoiceLeftRemoveDelay = voice_left_remove_delay; +	mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay);  	mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);  	LLVoiceClient::getInstance()->addObserver(this); @@ -135,6 +118,7 @@ LLCallFloater::LLCallFloater(const LLSD& key)  LLCallFloater::~LLCallFloater()  {  	resetVoiceRemoveTimers(); +	delete mSpeakerDelayRemover;  	delete mParticipants;  	mParticipants = NULL; @@ -648,33 +632,11 @@ void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state)  void LLCallFloater::setVoiceRemoveTimer(const LLUUID& voice_speaker_id)  { - -	// If there is already a started timer for the current panel don't do anything. -	bool no_timer_for_current_panel = true; -	if (mVoiceLeftTimersMap.size() > 0) -	{ -		timers_map::iterator found_it = mVoiceLeftTimersMap.find(voice_speaker_id); -		if (found_it != mVoiceLeftTimersMap.end()) -		{ -			no_timer_for_current_panel = false; -		} -	} - -	if (no_timer_for_current_panel) -	{ -		// Starting a timer to remove an avatar row panel after timeout -		mVoiceLeftTimersMap.insert(timer_pair(voice_speaker_id, -			new LLAvatarListItemRemoveTimer(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), mVoiceLeftRemoveDelay, voice_speaker_id))); -	} +	mSpeakerDelayRemover->setActionTimer(voice_speaker_id);  } -void LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id) +bool LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id)  { -	if (mVoiceLeftTimersMap.size() > 0) -	{ -		mVoiceLeftTimersMap.erase(mVoiceLeftTimersMap.find(voice_speaker_id)); -	} -  	LLAvatarList::uuid_vector_t& speaker_uuids = mAvatarList->getIDs();  	LLAvatarList::uuid_vector_t::iterator pos = std::find(speaker_uuids.begin(), speaker_uuids.end(), voice_speaker_id);  	if(pos != speaker_uuids.end()) @@ -682,34 +644,19 @@ void LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id)  		speaker_uuids.erase(pos);  		mAvatarList->setDirty();  	} + +	return false;  }  void LLCallFloater::resetVoiceRemoveTimers()  { -	if (mVoiceLeftTimersMap.size() > 0) -	{ -		for (timers_map::iterator iter = mVoiceLeftTimersMap.begin(); -			iter != mVoiceLeftTimersMap.end(); ++iter) -		{ -			delete iter->second; -		} -	} -	mVoiceLeftTimersMap.clear(); +	mSpeakerDelayRemover->removeAllTimers();  }  void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id)  { -	// Remove the timer if it has been already started -	if (mVoiceLeftTimersMap.size() > 0) -	{ -		timers_map::iterator found_it = mVoiceLeftTimersMap.find(voice_speaker_id); -		if (found_it != mVoiceLeftTimersMap.end()) -		{ -			delete found_it->second; -			mVoiceLeftTimersMap.erase(found_it); -		} -	} +	mSpeakerDelayRemover->unsetActionTimer(voice_speaker_id);  }  bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id) diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 096594aaa2..eded3a426b 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -44,6 +44,8 @@ class LLNonAvatarCaller;  class LLOutputMonitorCtrl;  class LLParticipantList;  class LLSpeakerMgr; +class LLSpeakersDelayActionsStorage; +  /**   * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron on the Speak button.   * It can be torn-off and freely positioned onscreen. @@ -169,7 +171,7 @@ private:  	 *  	 * @param voice_speaker_id LLUUID of Avatar List item to be removed from the list.  	 */ -	void removeVoiceLeftParticipant(const LLUUID& voice_speaker_id); +	bool removeVoiceLeftParticipant(const LLUUID& voice_speaker_id);  	/**  	 * Deletes all timers from the list to prevent started timers from ticking after destruction @@ -240,32 +242,11 @@ private:  	boost::signals2::connection mAvatarListRefreshConnection; +  	/** -	 * class LLAvatarListItemRemoveTimer -	 *  -	 * Implements a timer that removes avatar list item of a participant -	 * who has left the call. +	 * time out speakers when they are not part of current session  	 */ -	class LLAvatarListItemRemoveTimer : public LLEventTimer -	{ -	public: -		typedef boost::function<void(const LLUUID&)> callback_t; - -		LLAvatarListItemRemoveTimer(callback_t remove_cb, F32 period, const LLUUID& speaker_id); -		virtual ~LLAvatarListItemRemoveTimer() {}; - -		virtual BOOL tick(); - -	private: -		callback_t		mRemoveCallback; -		LLUUID			mSpeakerId; -	}; - -	typedef std::pair<LLUUID, LLAvatarListItemRemoveTimer*> timer_pair; -	typedef std::map<LLUUID, LLAvatarListItemRemoveTimer*> timers_map; - -	timers_map		mVoiceLeftTimersMap; -	S32				mVoiceLeftRemoveDelay; +	LLSpeakersDelayActionsStorage* mSpeakerDelayRemover;  	/**  	 * Stores reference to current voice channel. diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 8da207f887..f1de4e2982 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -1272,6 +1272,7 @@ bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index)  		chiclet->setChicletSizeChangedCallback(boost::bind(&LLChicletPanel::onChicletSizeChanged, this, _1, index));  		arrange(); +		LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, chiclet);  		return true;  	} @@ -1299,6 +1300,7 @@ void LLChicletPanel::removeChiclet(chiclet_list_t::iterator it)  	mChicletList.erase(it);  	arrange(); +	LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, chiclet);  	chiclet->die();  } diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index fb94657278..0e42ff09d8 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -51,6 +51,7 @@  #include "llinventorymodel.h"  #include "llfloaterworldmap.h"  #include "lllandmarkactions.h" +#include "llnotificationsutil.h"  #include "llsidetray.h"  #include "lltoggleablemenu.h"  #include "llviewerinventory.h" @@ -975,6 +976,10 @@ BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)  void copy_slurl_to_clipboard_cb(std::string& slurl)  {  	gClipboard.copyFromString(utf8str_to_wstring(slurl)); + +	LLSD args; +	args["SLURL"] = slurl; +	LLNotificationsUtil::add("CopySLURL", args);  } diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index e0f2fca580..2efae0c8db 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -277,13 +277,8 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)  				{  					object_owner.append("Unknown");  				} -				childSetText("object_name", object_owner); -				std::string owner_link = -					LLSLURL::buildCommand("agent", mObjectID, "inspect"); -				childSetText("owner_name", owner_link); -				childSetText("abuser_name_edit", object_owner); -				mAbuserID = object_id; -				mOwnerName = object_owner; + +				setFromAvatar(object_id, object_owner);  			}  			else  			{ @@ -305,7 +300,6 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)  	}  } -  void LLFloaterReporter::onClickSelectAbuser()  {  	gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE )); @@ -323,6 +317,17 @@ void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names,  } +void LLFloaterReporter::setFromAvatar(const LLUUID& avatar_id, const std::string& avatar_name) +{ +	mAbuserID = mObjectID = avatar_id; +	mOwnerName = avatar_name; + +	std::string avatar_link = LLSLURL::buildCommand("agent", mObjectID, "inspect"); +	childSetText("owner_name", avatar_link); +	childSetText("object_name", avatar_name); // name +	childSetText("abuser_name_edit", avatar_name); +} +  // static  void LLFloaterReporter::onClickSend(void *userdata)  { @@ -458,9 +463,8 @@ void LLFloaterReporter::showFromMenu(EReportType report_type)  	}  } -  // static -void LLFloaterReporter::showFromObject(const LLUUID& object_id) +void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_name)  {  	LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter"); @@ -469,8 +473,11 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id)  	LLAgentUI::buildFullname(fullname);  	f->childSetText("reporter_field", fullname); -	// Request info for this object -	f->getObjectInfo(object_id); +	if (avatar_name.empty()) +		// Request info for this object +		f->getObjectInfo(object_id); +	else +		f->setFromAvatar(object_id, avatar_name);  	// Need to deselect on close  	f->mDeselectOnClose = TRUE; @@ -479,6 +486,18 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id)  } +// static +void LLFloaterReporter::showFromObject(const LLUUID& object_id) +{ +	show(object_id); +} + +// static +void LLFloaterReporter::showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name) +{ +	show(avatar_id, avatar_name); +} +  void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id)  {  	childSetText("object_name", object_name); diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index cc2dfb2f98..7c6473f975 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -93,6 +93,7 @@ public:  	static void showFromMenu(EReportType report_type);  	static void showFromObject(const LLUUID& object_id); +	static void showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name);  	static void onClickSend			(void *userdata);  	static void onClickCancel		(void *userdata); @@ -109,6 +110,8 @@ public:  	void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id);  private: +	static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null); +  	void takeScreenshot();  	void sendReportViaCaps(std::string url);  	void uploadImage(); @@ -121,6 +124,7 @@ private:  	void enableControls(BOOL own_avatar);  	void getObjectInfo(const LLUUID& object_id);  	void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids); +	void setFromAvatar(const LLUUID& avatar_id, const std::string& avatar_name = LLStringUtil::null);  private:  	EReportType		mReportType; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index a63fb73032..b833c611bf 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1391,6 +1391,7 @@ void LLFolderView::startRenamingSelectedItem( void )  		// set focus will fail unless item is visible  		mRenamer->setFocus( TRUE );  		mRenamer->setTopLostCallback(boost::bind(onRenamerLost, _1)); +		mRenamer->setFocusLostCallback(boost::bind(onRenamerLost, _1));  		gFocusMgr.setTopCtrl( mRenamer );  	}  } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index e06e0c94ec..73597e7de3 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -110,6 +110,8 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id)  		}  	}  	setOverlapsScreenChannel(true); + +	LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);  }  void LLIMFloater::onFocusLost() @@ -228,6 +230,7 @@ void LLIMFloater::sendMsg()  LLIMFloater::~LLIMFloater()  { +	LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);  }  //virtual @@ -513,14 +516,14 @@ bool LLIMFloater::toggle(const LLUUID& session_id)  	if(!isChatMultiTab())  	{  		LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -		if (floater && floater->getVisible()) +		if (floater && floater->getVisible() && floater->hasFocus())  		{  			// clicking on chiclet to close floater just hides it to maintain existing  			// scroll/text entry state  			floater->setVisible(false);  			return false;  		} -		else if(floater && !floater->isDocked()) +		else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus()))  		{  			floater->setVisible(TRUE);  			floater->setFocus(TRUE); diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index d9db385d06..0ca0325451 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -116,6 +116,8 @@ public:  	static void onIMChicletCreated(const LLUUID& session_id); +	virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } +  private:  	// process focus events to set a currently active session  	/* virtual */ void onFocusLost(); diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 6cc985aef4..06a7b4a29c 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -35,19 +35,22 @@  #include "llimfloatercontainer.h"  #include "llfloaterreg.h" +#include "llimview.h" +#include "llavatariconctrl.h" +#include "llagent.h"  //  // LLIMFloaterContainer  //  LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) -:	LLMultiFloater(seed), -	mActiveVoiceFloater(NULL) +:	LLMultiFloater(seed)  {  	mAutoResize = FALSE;  }  LLIMFloaterContainer::~LLIMFloaterContainer()  { +	LLGroupMgr::getInstance()->removeObserver(this);  }  BOOL LLIMFloaterContainer::postBuild() @@ -87,13 +90,84 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp,  	LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); -	// make sure active voice icon shows up for new tab -	if (floaterp == mActiveVoiceFloater) +	LLUUID session_id = floaterp->getKey(); + +	if(gAgent.isInGroup(session_id)) +	{ +		mSessions[session_id] = floaterp; +		mID = session_id; +		mGroupID.push_back(session_id); +		LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(session_id); +		LLGroupMgr* gm = LLGroupMgr::getInstance(); +		gm->addObserver(this); + +		if (group_data && group_data->mInsigniaID.notNull()) +		{ +			mTabContainer->setTabImage(get_ptr_in_map(mSessions, session_id), group_data->mInsigniaID); +		} +		else +		{ +			gm->sendGroupPropertiesRequest(session_id); +		} +	} +	else +	{ +		LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id); +		LLAvatarPropertiesProcessor& app = LLAvatarPropertiesProcessor::instance(); +		app.addObserver(avatar_id, this); +		floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, avatar_id)); +		mSessions[avatar_id] = floaterp; + +		LLUUID* icon_id_ptr = LLAvatarIconIDCache::getInstance()->get(avatar_id); +		if(!icon_id_ptr) +		{ +			app.sendAvatarPropertiesRequest(avatar_id); +		} +		else +		{ +			mTabContainer->setTabImage(floaterp, *icon_id_ptr); +		} +	} +} + +void LLIMFloaterContainer::processProperties(void* data, enum EAvatarProcessorType type) +{ +	if (APT_PROPERTIES == type) +	{ +			LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data); +			if (avatar_data) +			{ +				LLUUID avatar_id = avatar_data->avatar_id; +				if(avatar_data->image_id != *LLAvatarIconIDCache::getInstance()->get(avatar_id)) +				{ +					LLAvatarIconIDCache::getInstance()->add(avatar_id,avatar_data->image_id); +				} +				mTabContainer->setTabImage(get_ptr_in_map(mSessions, avatar_id), avatar_data->image_id); +			} +	} +} + +void LLIMFloaterContainer::changed(LLGroupChange gc) +{ +	if (GC_PROPERTIES == gc)  	{ -		mTabContainer->setTabImage(floaterp, "active_voice_tab.tga");	 +		for(groupIDs_t::iterator it = mGroupID.begin(); it!=mGroupID.end(); it++) +		{ +			LLUUID group_id = *it; +			LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(group_id); +			if (group_data && group_data->mInsigniaID.notNull()) +			{ +				mTabContainer->setTabImage(get_ptr_in_map(mSessions, group_id), group_data->mInsigniaID); +			} +		}  	}  } +void LLIMFloaterContainer::onCloseFloater(LLUUID id) +{ +	LLAvatarPropertiesProcessor::instance().removeObserver(id, this); +} +  LLIMFloaterContainer* LLIMFloaterContainer::findInstance()  {  	return LLFloaterReg::findTypedInstance<LLIMFloaterContainer>("im_container"); diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index d4a542dfc2..1333b098bc 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -33,12 +33,17 @@  #ifndef LL_LLIMFLOATERCONTAINER_H  #define LL_LLIMFLOATERCONTAINER_H +#include <map> +#include <vector> +  #include "llfloater.h"  #include "llmultifloater.h" +#include "llavatarpropertiesprocessor.h" +#include "llgroupmgr.h"  class LLTabContainer; -class LLIMFloaterContainer : public LLMultiFloater +class LLIMFloaterContainer : public LLMultiFloater, public LLAvatarPropertiesObserver, public LLGroupMgrObserver  {  public:  	LLIMFloaterContainer(const LLSD& seed); @@ -51,15 +56,23 @@ public:  								BOOL select_added_floater,   								LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); +	void processProperties(void* data, EAvatarProcessorType type); +	void changed(LLGroupChange gc); +  	static LLFloater* getCurrentVoiceFloater();  	static LLIMFloaterContainer* findInstance();  	static LLIMFloaterContainer* getInstance(); -protected: -	 -	LLFloater* mActiveVoiceFloater; +private: +	typedef std::map<LLUUID,LLPanel*> avatarID_panel_map_t; +	avatarID_panel_map_t mSessions; + +	typedef std::vector<LLUUID> groupIDs_t; +	groupIDs_t mGroupID; + +	void onCloseFloater(LLUUID avatar_id);  };  #endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ff20a55358..c2a7969c0d 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -55,6 +55,7 @@  #include "llfloaterchatterbox.h"  #include "llimfloater.h"  #include "llgroupiconctrl.h" +#include "llmd5.h"  #include "llmutelist.h"  #include "llrecentpeople.h"  #include "llviewermessage.h" @@ -215,12 +216,14 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&  		mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID);  	} +	buildHistoryFileName(); +  	if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )  	{  		std::list<LLSD> chat_history;  		//involves parsing of a chat history -		LLLogChat::loadAllHistory(mName, chat_history); +		LLLogChat::loadAllHistory(mHistoryFileName, chat_history);  		addMessagesFromHistory(chat_history);  	}  } @@ -467,6 +470,44 @@ bool LLIMModel::LLIMSession::isOtherParticipantAvaline()  	return !mOtherParticipantIsAvatar;  } +void LLIMModel::LLIMSession::buildHistoryFileName() +{ +	mHistoryFileName = mName; +	 +	//ad-hoc requires sophisticated chat history saving schemes +	if (isAdHoc()) +	{ +		//in case of outgoing ad-hoc sessions +		if (mInitialTargetIDs.size()) +		{ +			std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); +			mHistoryFileName = mName + " hash" + generateHash(sorted_uuids); +			return; +		} +		 +		//in case of incoming ad-hoc sessions +		mHistoryFileName = mName + " " + LLLogChat::timestamp(true) + " " + mSessionID.asString().substr(0, 4); +	} +} + +//static +std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids) +{ +	LLMD5 md5_uuid; +	 +	std::set<LLUUID>::const_iterator it = sorted_uuids.begin(); +	while (it != sorted_uuids.end()) +	{ +		md5_uuid.update((unsigned char*)(*it).mData, 16); +		it++; +	} +	md5_uuid.finalize(); + +	LLUUID participants_md5_hash; +	md5_uuid.raw_digest((unsigned char*) participants_md5_hash.mData); +	return participants_md5_hash.asString(); +} +  void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id)  { @@ -614,11 +655,11 @@ bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from,  	return true;  } -bool LLIMModel::logToFile(const std::string& session_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) +bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)  {  	if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))  	{ -		LLLogChat::saveHistory(session_name, from, from_id, utf8_text); +		LLLogChat::saveHistory(file_name, from, from_id, utf8_text);  		return true;  	}  	else @@ -629,15 +670,7 @@ bool LLIMModel::logToFile(const std::string& session_name, const std::string& fr  bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)  { -	if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) -	{ -		LLLogChat::saveHistory(LLIMModel::getInstance()->getName(session_id), from, from_id, utf8_text); -		return true; -	} -	else -	{ -		return false; -	} +	return logToFile(LLIMModel::getInstance()->getHistoryFileName(session_id), from, from_id, utf8_text);  }  bool LLIMModel::proccessOnlineOfflineNotification( @@ -782,6 +815,18 @@ LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const  	return session->mSpeakers;  } +const std::string& LLIMModel::getHistoryFileName(const LLUUID& session_id) const +{ +	LLIMSession* session = findIMSession(session_id); +	if (!session) +	{ +		llwarns << "session " << session_id << " does not exist " << llendl; +		return LLStringUtil::null; +	} + +	return session->mHistoryFileName; +} +  // TODO get rid of other participant ID  void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing)  diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index a226d66b12..a3b4f78af0 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -69,6 +69,8 @@ public:  		void addMessagesFromHistory(const std::list<LLSD>& history);  		void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time);  		void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction); +		 +		/** @deprecated */  		static void chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata);  		bool isAdHoc(); @@ -80,12 +82,20 @@ public:  		bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;}  		bool isAvalineSessionType() const { return mSessionType == AVALINE_SESSION;} +		//*TODO make private +		/** ad-hoc sessions involve sophisticated chat history file naming schemes */ +		void buildHistoryFileName(); + +		//*TODO make private +		static std::string generateHash(const std::set<LLUUID>& sorted_uuids); +  		LLUUID mSessionID;  		std::string mName;  		EInstantMessage mType;  		SType mSessionType;  		LLUUID mOtherParticipantID;  		std::vector<LLUUID> mInitialTargetIDs; +		std::string mHistoryFileName;  		// connection to voice channel state change signal  		boost::signals2::connection mVoiceChannelStateChangeConnection; @@ -231,6 +241,8 @@ public:  	*/  	LLIMSpeakerMgr* getSpeakerManager(const LLUUID& session_id) const; +	const std::string& getHistoryFileName(const LLUUID& session_id) const; +  	static void sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id);  	static bool sendStartSession(const LLUUID& temp_session_id, const LLUUID& other_participant_id,  						  const std::vector<LLUUID>& ids, EInstantMessage dialog); @@ -243,7 +255,7 @@ public:  	/**  	 * Saves an IM message into a file  	 */ -	bool logToFile(const std::string& session_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); +	bool logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text);  private: diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 0374a1d25b..4b0539337b 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -689,7 +689,7 @@ void LLInspectAvatar::onToggleMute()  void LLInspectAvatar::onClickReport()  { -	LLFloaterReporter::showFromObject(mAvatarID); +	LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName);  	closeFloater();  } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 1b1f615a57..f91bbaff68 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3687,18 +3687,6 @@ BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,  	return rv;  } -BOOL LLCallingCardBridge::removeItem() -{ -	if (LLFriendCardsManager::instance().isItemInAnyFriendsList(getItem())) -	{ -		LLAvatarActions::removeFriendDialog(getItem()->getCreatorUUID()); -		return FALSE; -	} -	else -	{ -		return LLItemBridge::removeItem(); -	} -}  // +=================================================+  // |        LLNotecardBridge                         |  // +=================================================+ diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index fced0047e8..759d0cba18 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -448,7 +448,6 @@ public:  							EDragAndDropType cargo_type,  							void* cargo_data);  	void refreshFolderViewItem(); -	BOOL removeItem();  protected:  	LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ); diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 404e266806..7f49a7defb 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -43,8 +43,11 @@  #include "lltrans.h"  #include "lluictrlfactory.h"  #include "lltooltip.h" +#include "llnotificationsutil.h" +#include "llregionflags.h"  // newview includes +#include "llagent.h"  #include "llinventoryobserver.h"  #include "lllandmarkactions.h"  #include "lllandmarklist.h" @@ -56,6 +59,7 @@  #include "lltrans.h"  #include "llviewerinventory.h"  #include "llviewerparcelmgr.h" +#include "llviewerregion.h"  #include "llviewercontrol.h"  #include "llviewermenu.h"  #include "llurllineeditorctrl.h" @@ -256,36 +260,42 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)  	voice_icon.tool_tip = LLTrans::getString("LocationCtrlVoiceTooltip");  	voice_icon.mouse_opaque = true;  	mParcelIcon[VOICE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(voice_icon); +	mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, VOICE_ICON));  	addChild(mParcelIcon[VOICE_ICON]);  	LLIconCtrl::Params fly_icon = p.fly_icon;  	fly_icon.tool_tip = LLTrans::getString("LocationCtrlFlyTooltip");  	fly_icon.mouse_opaque = true;  	mParcelIcon[FLY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(fly_icon); +	mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, FLY_ICON));  	addChild(mParcelIcon[FLY_ICON]);  	LLIconCtrl::Params push_icon = p.push_icon;  	push_icon.tool_tip = LLTrans::getString("LocationCtrlPushTooltip");  	push_icon.mouse_opaque = true;  	mParcelIcon[PUSH_ICON] = LLUICtrlFactory::create<LLIconCtrl>(push_icon); +	mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PUSH_ICON));  	addChild(mParcelIcon[PUSH_ICON]);  	LLIconCtrl::Params build_icon = p.build_icon;  	build_icon.tool_tip = LLTrans::getString("LocationCtrlBuildTooltip");  	build_icon.mouse_opaque = true;  	mParcelIcon[BUILD_ICON] = LLUICtrlFactory::create<LLIconCtrl>(build_icon); +	mParcelIcon[BUILD_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, BUILD_ICON));  	addChild(mParcelIcon[BUILD_ICON]);  	LLIconCtrl::Params scripts_icon = p.scripts_icon;  	scripts_icon.tool_tip = LLTrans::getString("LocationCtrlScriptsTooltip");  	scripts_icon.mouse_opaque = true;  	mParcelIcon[SCRIPTS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(scripts_icon); +	mParcelIcon[SCRIPTS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SCRIPTS_ICON));  	addChild(mParcelIcon[SCRIPTS_ICON]);  	LLIconCtrl::Params damage_icon = p.damage_icon;  	damage_icon.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip");  	damage_icon.mouse_opaque = true;  	mParcelIcon[DAMAGE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(damage_icon); +	mParcelIcon[DAMAGE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, DAMAGE_ICON));  	addChild(mParcelIcon[DAMAGE_ICON]);  	LLTextBox::Params damage_text = p.damage_text; @@ -918,3 +928,45 @@ bool LLLocationInputCtrl::onLocationContextMenuItemEnabled(const LLSD& userdata)  	return false;  } + +void LLLocationInputCtrl::onParcelIconClick(EParcelIcon icon) +{ +	switch (icon) +	{ +	case VOICE_ICON: +		LLNotificationsUtil::add("NoVoice"); +		break; +	case FLY_ICON: +		LLNotificationsUtil::add("NoFly"); +		break; +	case PUSH_ICON: +		LLNotificationsUtil::add("PushRestricted"); +		break; +	case BUILD_ICON: +		LLNotificationsUtil::add("NoBuild"); +		break; +	case SCRIPTS_ICON: +	{ +		LLViewerRegion* region = gAgent.getRegion(); +		if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS) +		{ +			LLNotificationsUtil::add("ScriptsStopped"); +		} +		else if(region && region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS) +		{ +			LLNotificationsUtil::add("ScriptsNotRunning"); +		} +		else +		{ +			LLNotificationsUtil::add("NoOutsideScripts"); +		} +		break; +	} +	case DAMAGE_ICON: +		LLNotificationsUtil::add("NotSafe"); +		break; +	case ICON_COUNT: +		break; +	// no default to get compiler warning when a new icon gets added +	} +} diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 7959fab2de..607ccd4da6 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -102,6 +102,18 @@ public:  	void					handleLoginComplete();  private: + +	enum EParcelIcon +	{ +		VOICE_ICON = 0, +		FLY_ICON, +		PUSH_ICON, +		BUILD_ICON, +		SCRIPTS_ICON, +		DAMAGE_ICON, +		ICON_COUNT +	}; +  	friend class LLUICtrlFactory;  	LLLocationInputCtrl(const Params&);  	virtual ~LLLocationInputCtrl(); @@ -138,6 +150,7 @@ private:  	// callbacks  	bool					onLocationContextMenuItemEnabled(const LLSD& userdata);  	void 					onLocationContextMenuItemClicked(const LLSD& userdata); +	void					onParcelIconClick(EParcelIcon icon);  	LLMenuGL*				mLocationContextMenu;  	LLButton*				mAddLandmarkBtn; @@ -146,16 +159,6 @@ private:  	S32						mIconHPad;			// pad between all icons  	S32						mAddLandmarkHPad;	// pad to left of landmark star -	enum EParcelIcon -	{ -		VOICE_ICON = 0, -		FLY_ICON, -		PUSH_ICON, -		BUILD_ICON, -		SCRIPTS_ICON, -		DAMAGE_ICON, -		ICON_COUNT -	};  	LLIconCtrl*	mParcelIcon[ICON_COUNT];  	LLTextBox* mDamageText; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 4e5aaeb66a..dc187bf36c 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -237,15 +237,15 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line)  	messages.back()[IM_TEXT] = im_text;  } -void LLLogChat::loadAllHistory(const std::string& session_name, std::list<LLSD>& messages) +void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& messages)  { -	if (session_name.empty()) +	if (file_name.empty())  	{  		llwarns << "Session name is Empty!" << llendl;  		return ;  	} -	LLFILE* fptr = LLFile::fopen(makeLogFileName(session_name), "r");		/*Flawfinder: ignore*/ +	LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r");		/*Flawfinder: ignore*/  	if (!fptr) return;	//No previous conversation with this name.  	char buffer[LOG_RECALL_SIZE];		/*Flawfinder: ignore*/ diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 3d3f5c4458..4290e4bbc0 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -56,7 +56,7 @@ public:  		                    void (*callback)(ELogLineType, const LLSD&, void*),   							void* userdata); -	static void loadAllHistory(const std::string& session_name, std::list<LLSD>& messages); +	static void loadAllHistory(const std::string& file_name, std::list<LLSD>& messages);  private:  	static std::string cleanFileName(std::string filename);  }; diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index 569d3001bf..c30ef3221d 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -356,13 +356,6 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)  	for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it)  		(*it)->setGroupID(group_id); -	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); -	if(gdatap) -	{ -		childSetValue("group_name", gdatap->mName); -		childSetToolTip("group_name",gdatap->mName); -	} -  	LLButton* button_apply = findChild<LLButton>("btn_apply");  	LLButton* button_refresh = findChild<LLButton>("btn_refresh");  	LLButton* button_create = findChild<LLButton>("btn_create"); @@ -464,6 +457,17 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)  	}  	reposButtons(); + +	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); + +	if(gdatap) +	{ +		childSetValue("group_name", gdatap->mName); +		childSetToolTip("group_name",gdatap->mName); +		 +		//group data is already present, call update manually +		update(GC_ALL); +	}  }  bool LLPanelGroup::apply(LLPanelGroupTab* tab) diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 0e55ff3214..45f0381d6f 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -452,6 +452,7 @@ LLPanelGroupSubTab::LLPanelGroupSubTab()  :	LLPanelGroupTab(),  	mHeader(NULL),  	mFooter(NULL), +	mActivated(false),  	mSearchEditor(NULL)  {  } @@ -504,13 +505,14 @@ void LLPanelGroupSubTab::setGroupID(const LLUUID& id)  		mSearchEditor->clear();  		setSearchFilter("");  	} + +	mActivated = false;  }  void LLPanelGroupSubTab::setSearchFilter(const std::string& filter)  {  	if(mSearchFilter == filter)  		return; -	lldebugs << "LLPanelGroupSubTab::setSearchFilter() ==> '" << filter << "'" << llendl;  	mSearchFilter = filter;  	LLStringUtil::toLower(mSearchFilter);  	update(GC_ALL); @@ -518,13 +520,11 @@ void LLPanelGroupSubTab::setSearchFilter(const std::string& filter)  void LLPanelGroupSubTab::activate()  { -	lldebugs << "LLPanelGroupSubTab::activate()" << llendl;  	setOthersVisible(TRUE);  }  void LLPanelGroupSubTab::deactivate()  { -	lldebugs << "LLPanelGroupSubTab::deactivate()" << llendl;  	setOthersVisible(FALSE);  } @@ -534,19 +534,11 @@ void LLPanelGroupSubTab::setOthersVisible(BOOL b)  	{  		mHeader->setVisible( b );  	} -	else -	{ -		llwarns << "LLPanelGroupSubTab missing header!" << llendl; -	}  	if (mFooter)  	{  		mFooter->setVisible( b );  	} -	else -	{ -		llwarns << "LLPanelGroupSubTab missing footer!" << llendl; -	}  }  bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action) @@ -875,10 +867,12 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()  	for (itor = selection.begin();  		 itor != selection.end(); ++itor)  	{ -		selected_members.push_back( (*itor)->getUUID() ); +		LLUUID member_id = (*itor)->getValue()["uuid"]; + +		selected_members.push_back( member_id );  		// Get this member's power mask including any unsaved changes -		U64 powers = getAgentPowersBasedOnRoleChanges((*itor)->getUUID()); +		U64 powers = getAgentPowersBasedOnRoleChanges( member_id );  		allowed_by_all &= powers;  		allowed_by_some |= powers; @@ -1022,6 +1016,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()  				//last owner.  We should check for this special case  				// -jwolk  				check->setEnabled(cb_enable); +				item->setEnabled(cb_enable);  			}  		}  		else @@ -1098,7 +1093,8 @@ void LLPanelGroupMembersSubTab::handleEjectMembers()  	for (itor = selection.begin() ;   		 itor != selection.end(); ++itor)  	{ -		selected_members.push_back((*itor)->getUUID()); +		LLUUID member_id = (*itor)->getValue()["uuid"]; +		selected_members.push_back( member_id );  	}  	mMembersList->deleteSelectedItems(); @@ -1154,7 +1150,8 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,  	for (std::vector<LLScrollListItem*>::iterator itor = selection.begin() ;   		 itor != selection.end(); ++itor)  	{ -		member_id = (*itor)->getUUID(); + +		member_id = (*itor)->getValue()["uuid"];  		//see if we requested a change for this member before  		if ( mMemberRoleChangeData.find(member_id) == mMemberRoleChangeData.end() ) @@ -1245,15 +1242,19 @@ void LLPanelGroupMembersSubTab::handleMemberDoubleClick()  	LLScrollListItem* selected = mMembersList->getFirstSelected();  	if (selected)  	{ -		LLAvatarActions::showProfile(selected->getUUID()); +		LLUUID member_id = selected->getValue()["uuid"]; +		LLAvatarActions::showProfile( member_id );  	}  }  void LLPanelGroupMembersSubTab::activate()  {  	LLPanelGroupSubTab::activate(); - -	update(GC_ALL); +	if(!mActivated) +	{ +		update(GC_ALL); +		mActivated = true; +	}  }  void LLPanelGroupMembersSubTab::deactivate() @@ -1629,7 +1630,9 @@ void LLPanelGroupMembersSubTab::updateMembers()  			row["columns"][2]["value"] = mMemberProgress->second->getOnlineStatus();  			row["columns"][2]["font"] = "SANSSERIF_SMALL"; -			mMembersList->addElement(row);//, ADD_SORTED); +			LLScrollListItem* member = mMembersList->addElement(row);//, ADD_SORTED); + +			LLUUID id = member->getValue()["uuid"];  			mHasMatch = TRUE;  		}  	} diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 2f81900e60..eac22a6338 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -149,6 +149,8 @@ protected:  	icon_map_t	mActionIcons; +	bool mActivated; +  	void setOthersVisible(BOOL b);  }; diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index b1cdb4d81f..a334eb9d68 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -245,7 +245,6 @@ void LLPanelIMControlPanel::nameUpdatedCallback(const LLUUID& id, const std::str  LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id):  mParticipantList(NULL)  { -	mSpeakerManager = LLIMModel::getInstance()->getSpeakerManager(session_id);  }  BOOL LLPanelGroupControlPanel::postBuild() @@ -264,9 +263,6 @@ LLPanelGroupControlPanel::~LLPanelGroupControlPanel()  // virtual  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(); @@ -307,7 +303,10 @@ void LLPanelGroupControlPanel::setSessionId(const LLUUID& 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"), true,false); +	{ +		LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(session_id); +		mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true,false); +	}  } diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index c18be5a6df..25fdf944c9 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -37,8 +37,6 @@  #include "llvoicechannel.h"  #include "llcallingcard.h" -class LLSpeakerMgr; -class LLAvatarList;  class LLParticipantList;  class LLPanelChatControlPanel : public LLPanel @@ -110,7 +108,6 @@ public:  protected:  	LLUUID mGroupID; -	LLSpeakerMgr* mSpeakerManager;  	LLParticipantList* mParticipantList; diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index b80eb9db38..0c10f11bfc 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -34,26 +34,20 @@  #include "llpanelplaceinfo.h" -#include "roles_constants.h"  #include "llsdutil.h" -#include "llsecondlifeurls.h"  #include "llsdutil_math.h" +  #include "llregionhandle.h" -#include "message.h"  #include "lliconctrl.h"  #include "lltextbox.h"  #include "llagent.h" -#include "llavatarpropertiesprocessor.h"  #include "llexpandabletextbox.h"  #include "llpanelpick.h"  #include "lltexturectrl.h" -#include "llviewerinventory.h" -#include "llviewerparcelmgr.h"  #include "llviewerregion.h" -#include "llviewertexteditor.h"  LLPanelPlaceInfo::LLPanelPlaceInfo()  :	LLPanel(), @@ -265,25 +259,6 @@ void LLPanelPlaceInfo::reshape(S32 width, S32 height, BOOL called_from_parent)  	}  } -// virtual -void LLPanelPlaceInfo::handleVisibilityChange(BOOL new_visibility) -{ -	LLPanel::handleVisibilityChange(new_visibility); - -	LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); -	if (!parcel_mgr) -		return; - -	// Remove land selection when panel hides. -	if (!new_visibility) -	{ -		if (!parcel_mgr->selectionEmpty()) -		{ -			parcel_mgr->deselectLand(); -		} -	} -} -  void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPickEdit* pick_panel)  {  	std::string region_name = mRegionName->getText(); diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 7dfc7b2444..3091f7ed24 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -94,7 +94,6 @@ public:  	/*virtual*/ void processParcelInfo(const LLParcelData& parcel_data);  	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); -	/*virtual*/ void handleVisibilityChange (BOOL new_visibility);  	// Create a pick for the location specified  	// by global_pos. diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp index 402d50ba9c..d892e2885b 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -257,6 +257,25 @@ void LLPanelPlaceProfile::processParcelInfo(const LLParcelData& parcel_data)  	}  } +// virtual +void LLPanelPlaceProfile::handleVisibilityChange(BOOL new_visibility) +{ +	LLPanel::handleVisibilityChange(new_visibility); + +	LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); +	if (!parcel_mgr) +		return; + +	// Remove land selection when panel hides. +	if (!new_visibility) +	{ +		if (!parcel_mgr->selectionEmpty()) +		{ +			parcel_mgr->deselectUnused(); +		} +	} +} +  void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel,  													LLViewerRegion* region,  													const LLVector3d& pos_global, diff --git a/indra/newview/llpanelplaceprofile.h b/indra/newview/llpanelplaceprofile.h index 8c30ca92fb..8ca9526875 100644 --- a/indra/newview/llpanelplaceprofile.h +++ b/indra/newview/llpanelplaceprofile.h @@ -52,6 +52,8 @@ public:  	/*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); +	/*virtual*/ void handleVisibilityChange(BOOL new_visibility); +  	// Displays information about the currently selected parcel  	// without sending a request to the server.  	// If is_current_parcel true shows "You Are Here" banner. diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index b037674c37..a4f0e55a93 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -289,89 +289,92 @@ BOOL LLPanelPlaces::postBuild()  void LLPanelPlaces::onOpen(const LLSD& key)  { -	if(!mPlaceProfile || !mLandmarkInfo || key.size() == 0) +	if (!mPlaceProfile || !mLandmarkInfo)  		return; -	mFilterEditor->clear(); -	onFilterEdit("", false); - -	mPlaceInfoType = key["type"].asString(); -	mPosGlobal.setZero(); -	mItem = NULL; -	isLandmarkEditModeOn = false; -	togglePlaceInfoPanel(TRUE); - -	if (mPlaceInfoType == AGENT_INFO_TYPE) -	{ -		mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT); -	} -	else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE) +	if (key.size() != 0)  	{ -		mLandmarkInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK); +		mFilterEditor->clear(); +		onFilterEdit("", false); -		if (key.has("x") && key.has("y") && key.has("z")) +		mPlaceInfoType = key["type"].asString(); +		mPosGlobal.setZero(); +		mItem = NULL; +		isLandmarkEditModeOn = false; +		togglePlaceInfoPanel(TRUE); + +		if (mPlaceInfoType == AGENT_INFO_TYPE)  		{ -			mPosGlobal = LLVector3d(key["x"].asReal(), -									key["y"].asReal(), -									key["z"].asReal()); +			mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT);  		} -		else +		else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)  		{ -			mPosGlobal = gAgent.getPositionGlobal(); -		} - -		mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal); +			mLandmarkInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK); -		// Disable Save button because there is no item to save yet. -		// The button will be enabled in onLandmarkLoaded callback. -		mSaveBtn->setEnabled(FALSE); -	} -	else if (mPlaceInfoType == LANDMARK_INFO_TYPE) -	{ -		mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK); +			if (key.has("x") && key.has("y") && key.has("z")) +			{ +				mPosGlobal = LLVector3d(key["x"].asReal(), +										key["y"].asReal(), +										key["z"].asReal()); +			} +			else +			{ +				mPosGlobal = gAgent.getPositionGlobal(); +			} -		LLInventoryItem* item = gInventory.getItem(key["id"].asUUID()); -		if (!item) -			return; +			mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal); -		setItem(item); -	} -	else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) -	{ -		if (key.has("id")) +			// Disable Save button because there is no item to save yet. +			// The button will be enabled in onLandmarkLoaded callback. +			mSaveBtn->setEnabled(FALSE); +		} +		else if (mPlaceInfoType == LANDMARK_INFO_TYPE)  		{ -			LLUUID parcel_id = key["id"].asUUID(); -			mPlaceProfile->setParcelID(parcel_id); +			mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK); + +			LLInventoryItem* item = gInventory.getItem(key["id"].asUUID()); +			if (!item) +				return; -			// query the server to get the global 3D position of this -			// parcel - we need this for teleport/mapping functions. -			mRemoteParcelObserver->setParcelID(parcel_id); +			setItem(item);  		} -		else +		else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)  		{ -			mPosGlobal = LLVector3d(key["x"].asReal(), -									key["y"].asReal(), -									key["z"].asReal()); -			mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); +			if (key.has("id")) +			{ +				LLUUID parcel_id = key["id"].asUUID(); +				mPlaceProfile->setParcelID(parcel_id); + +				// query the server to get the global 3D position of this +				// parcel - we need this for teleport/mapping functions. +				mRemoteParcelObserver->setParcelID(parcel_id); +			} +			else +			{ +				mPosGlobal = LLVector3d(key["x"].asReal(), +										key["y"].asReal(), +										key["z"].asReal()); +				mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); +			} + +			mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE);  		} +		else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) +		{ +			S32 index = key["id"].asInteger(); -		mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE); -	} -	else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) -	{ -		S32 index = key["id"].asInteger(); +			const LLTeleportHistoryStorage::slurl_list_t& hist_items = +						LLTeleportHistoryStorage::getInstance()->getItems(); -		const LLTeleportHistoryStorage::slurl_list_t& hist_items = -					LLTeleportHistoryStorage::getInstance()->getItems(); +			mPosGlobal = hist_items[index].mGlobalPos; -		mPosGlobal = hist_items[index].mGlobalPos; +			mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY); +			mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); +		} -		mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY); -		mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal); +		updateVerbs();  	} -	updateVerbs(); -  	LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();  	if (!parcel_mgr)  		return; @@ -388,9 +391,12 @@ void LLPanelPlaces::onOpen(const LLSD& key)  	{  		parcel_mgr->removeObserver(mParcelObserver); +		// Clear the reference to selection to allow its removal in deselectUnused(). +		mParcel.clear(); +  		if (!parcel_mgr->selectionEmpty())  		{ -			parcel_mgr->deselectLand(); +			parcel_mgr->deselectUnused();  		}  	}  } @@ -765,23 +771,23 @@ void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param)  		mPickPanel->reshape(rect.getWidth(), rect.getHeight());  		mPickPanel->setRect(rect);  	} -    else if (item == "add_to_favbar") -    { -        if ( mItem.notNull() )  -        { -            const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); -            if ( favorites_id.notNull() ) -            { -                copy_inventory_item(gAgent.getID(), -                                    mItem->getPermissions().getOwner(), -                                    mItem->getUUID(), -                                    favorites_id, -                                    std::string(), -                                    LLPointer<LLInventoryCallback>(NULL)); -                llinfos << "Copied inventory item #" << mItem->getUUID() << " to favorites." << llendl; -            } -        } -    } +	else if (item == "add_to_favbar") +	{ +		if ( mItem.notNull() ) +		{ +			const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); +			if ( favorites_id.notNull() ) +			{ +				copy_inventory_item(gAgent.getID(), +									mItem->getPermissions().getOwner(), +									mItem->getUUID(), +									favorites_id, +									std::string(), +									LLPointer<LLInventoryCallback>(NULL)); +				llinfos << "Copied inventory item #" << mItem->getUUID() << " to favorites." << llendl; +			} +		} +	}  }  void LLPanelPlaces::onBackButtonClicked() @@ -826,6 +832,14 @@ void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible)  			mLandmarkInfo->setVisible(FALSE);  		} +		else if (mPlaceInfoType == AGENT_INFO_TYPE) +		{ +			LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver); + +			// Clear reference to parcel selection when closing place profile panel. +			// LLViewerParcelMgr removes the selection if it has 1 reference to it. +			mParcel.clear(); +		}  	}  	else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||  			 mPlaceInfoType == LANDMARK_INFO_TYPE) @@ -858,6 +872,20 @@ void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible)  	}  } +// virtual +void LLPanelPlaces::handleVisibilityChange(BOOL new_visibility) +{ +	LLPanel::handleVisibilityChange(new_visibility); + +	if (!new_visibility && mPlaceInfoType == AGENT_INFO_TYPE) +	{ +		LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver); + +		// Clear reference to parcel selection when closing places panel. +		mParcel.clear(); +	} +} +  void LLPanelPlaces::changedParcelSelection()  {  	if (!mPlaceProfile) diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 27b5911ebb..0eba7f3afc 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -97,6 +97,8 @@ private:  	void togglePickPanel(BOOL visible);  	void togglePlaceInfoPanel(BOOL visible); +	/*virtual*/ void handleVisibilityChange(BOOL new_visibility); +  	void updateVerbs();  	LLPanelPlaceInfo* getCurrentInfoPanel(); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index b049f914ad..88b706fb6b 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -125,6 +125,8 @@ LLParticipantList::~LLParticipantList()  		delete mParticipantListMenu;  		mParticipantListMenu = NULL;  	} + +	mAvatarList->setContextMenu(NULL);  }  void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible) @@ -431,6 +433,10 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu()  	LLContextMenu* main_menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(  		"menu_participant_list.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); +	// Don't show sort options for P2P chat +	bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); +	main_menu->setItemVisible("SortByName", is_sort_visible); +	main_menu->setItemVisible("SortByRecentSpeakers", is_sort_visible);  	main_menu->setItemVisible("Moderator Options", isGroupModerator());  	main_menu->arrangeAndClear(); @@ -456,11 +462,6 @@ 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) diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 0dd9203c6d..9608cd1263 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -44,7 +44,6 @@  #include "llvoavatar.h"  #include "llworld.h" -const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers  const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);  const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); @@ -73,8 +72,6 @@ LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerTy  	}  	gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id)); - -	mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);  } @@ -164,6 +161,89 @@ bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPo  	return(	lhs->mDisplayName.compare(rhs->mDisplayName) < 0 );  } +LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id) +: LLEventTimer(action_period) +, mActionCallback(action_cb) +, mSpeakerId(speaker_id) +{ +} + +BOOL LLSpeakerActionTimer::tick() +{ +	if (mActionCallback) +	{ +		return (BOOL)mActionCallback(mSpeakerId); +	} +	return TRUE; +} + +LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay) +: mActionCallback(action_cb) +, mActionDelay(action_delay) +{ +} + +LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage() +{ +	removeAllTimers(); +} + +void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id) +{ +	bool not_found = true; +	if (mActionTimersMap.size() > 0) +	{ +		not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end(); +	} + +	// If there is already a started timer for the passed UUID don't do anything. +	if (not_found) +	{ +		// Starting a timer to remove an participant after delay is completed +		mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id, +			new LLSpeakerActionTimer( +				boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1), +				mActionDelay, speaker_id))); +	} +} + +void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id) +{ +	if (mActionTimersMap.size() == 0) return; + +	LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id); + +	if (it_speaker != mActionTimersMap.end()) +	{ +		delete it_speaker->second; +		mActionTimersMap.erase(it_speaker); +	} +} + +void LLSpeakersDelayActionsStorage::removeAllTimers() +{ +	LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin(); +	for (; iter != mActionTimersMap.end(); ++iter) +	{ +		delete iter->second; +	} +	mActionTimersMap.clear(); +} + +bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id) +{ +	unsetActionTimer(speaker_id); + +	if (mActionCallback) +	{ +		mActionCallback(speaker_id); +	} + +	// do not return true to avoid deleting of an timer twice: +	// in LLSpeakersDelayActionsStorage::unsetActionTimer() & LLEventTimer::updateClass() +	return false; +} +  //  // LLSpeakerMgr @@ -172,10 +252,14 @@ bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPo  LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) :   	mVoiceChannel(channelp)  { +	static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0); + +	mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay);  }  LLSpeakerMgr::~LLSpeakerMgr()  { +	delete mSpeakerDelayRemover;  }  LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) @@ -198,7 +282,6 @@ LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin  		{  			// keep highest priority status (lowest value) instead of overriding current value  			speakerp->mStatus = llmin(speakerp->mStatus, status); -			speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);  			// RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id  			// we need to override speakers that we think are objects when we find out they are really  			// residents @@ -210,6 +293,8 @@ LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin  		}  	} +	mSpeakerDelayRemover->unsetActionTimer(speakerp->mID); +  	return speakerp;  } @@ -314,7 +399,7 @@ void LLSpeakerMgr::update(BOOL resort_ok)  	S32 sort_index = 0;  	speaker_list_t::iterator sorted_speaker_it;  	for(sorted_speaker_it = mSpeakersSorted.begin();  -		sorted_speaker_it != mSpeakersSorted.end(); ) +		sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)  	{  		LLPointer<LLSpeaker> speakerp = *sorted_speaker_it; @@ -327,19 +412,6 @@ void LLSpeakerMgr::update(BOOL resort_ok)  		// stuff sort ordinal into speaker so the ui can sort by this value  		speakerp->mSortIndex = sort_index++; - -		// remove speakers that have been gone too long -		if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired()) -		{ -			fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "remove"); - -			mSpeakers.erase(speakerp->mID); -			sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it); -		} -		else -		{ -			++sorted_speaker_it; -		}  	}  } @@ -363,6 +435,35 @@ void LLSpeakerMgr::updateSpeakerList()  	}  } +void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp) +{ +	speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; +	speakerp->mDotColor = INACTIVE_COLOR; +	mSpeakerDelayRemover->setActionTimer(speakerp->mID); +} + +bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id) +{ +	mSpeakers.erase(speaker_id); + +	speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin(); +	 +	for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) +	{ +		if (speaker_id == (*sorted_speaker_it)->mID) +		{ +			mSpeakersSorted.erase(sorted_speaker_it); +			break; +		} +	} + +	fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove"); + +	update(TRUE); + +	return false; +} +  LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)  {  	//In some conditions map causes crash if it is empty(Windows only), adding check (EK) @@ -511,9 +612,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)  			{  				if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull())  				{ -					speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; -					speakerp->mDotColor = INACTIVE_COLOR; -					speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); +					setSpeakerNotInChannel(speakerp);  				}  				else if (agent_data["transition"].asString() == "ENTER")  				{ @@ -563,9 +662,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)  			std::string agent_transition = update_it->second.asString();  			if (agent_transition == "LEAVE" && speakerp.notNull())  			{ -				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; -				speakerp->mDotColor = INACTIVE_COLOR; -				speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); +				setSpeakerNotInChannel(speakerp);  			}  			else if ( agent_transition == "ENTER")  			{ @@ -734,12 +831,13 @@ void LLActiveSpeakerMgr::updateSpeakerList()  	mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();  	// always populate from active voice channel -	if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) +	if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false  	{  		fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear");  		mSpeakers.clear();  		mSpeakersSorted.clear();  		mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); +		mSpeakerDelayRemover->removeAllTimers();  	}  	LLSpeakerMgr::updateSpeakerList(); @@ -800,9 +898,7 @@ void LLLocalSpeakerMgr::updateSpeakerList()  			LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);  			if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS)  			{ -				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; -				speakerp->mDotColor = INACTIVE_COLOR; -				speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); +				setSpeakerNotInChannel(speakerp);  			}  		}  	} diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index da8dfdf548..63237204c8 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -73,7 +73,6 @@ public:  	F32				mLastSpokeTime;		// timestamp when this speaker last spoke  	F32				mSpeechVolume;		// current speech amplitude (timea average rms amplitude?)  	std::string		mDisplayName;		// cache user name for this speaker -	LLFrameTimer	mActivityTimer;	// time out speakers when they are not part of current voice channel  	BOOL			mHasSpoken;			// has this speaker said anything this session?  	BOOL			mHasLeftCurrentCall;	// has this speaker left the current voice call?  	LLColor4		mDotColor; @@ -120,6 +119,92 @@ private:  	const LLUUID& mSpeakerID;  }; +/** + * class LLSpeakerActionTimer + *  + * Implements a timer that calls stored callback action for stored speaker after passed period. + * + * Action is called until callback returns "true". + * In this case the timer will be removed via LLEventTimer::updateClass(). + * Otherwise it should be deleted manually in place where it is used. + * If action callback is not set timer will tick only once and deleted. + */ +class LLSpeakerActionTimer : public LLEventTimer +{ +public: +	typedef boost::function<bool(const LLUUID&)>	action_callback_t; +	typedef std::map<LLUUID, LLSpeakerActionTimer*> action_timers_map_t; +	typedef action_timers_map_t::value_type			action_value_t; +	typedef action_timers_map_t::const_iterator		action_timer_const_iter_t; +	typedef action_timers_map_t::iterator			action_timer_iter_t; + +	/** +	 * Constructor. +	 * +	 * @param action_cb - callback which will be called each time after passed action period. +	 * @param action_period - time in seconds timer should tick. +	 * @param speaker_id - LLUUID of speaker which will be passed into action callback. +	 */ +	LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id); +	virtual ~LLSpeakerActionTimer() {}; + +	/** +	 * Implements timer "tick". +	 * +	 * If action callback is not specified returns true. Instance will be deleted by LLEventTimer::updateClass(). +	 */ +	virtual BOOL tick(); + +private: +	action_callback_t	mActionCallback; +	LLUUID				mSpeakerId; +}; + +/** + * Represents a functionality to store actions for speakers with delay. + * Is based on LLSpeakerActionTimer. + */ +class LLSpeakersDelayActionsStorage +{ +public: +	LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay); +	~LLSpeakersDelayActionsStorage(); + +	/** +	 * Sets new LLSpeakerActionTimer with passed speaker UUID. +	 */ +	void setActionTimer(const LLUUID& speaker_id); + +	/** +	 * Removes stored LLSpeakerActionTimer for passed speaker UUID from internal map and deletes it. +	 * +	 * @see onTimerActionCallback() +	 */ +	void unsetActionTimer(const LLUUID& speaker_id); + +	void removeAllTimers(); +private: +	/** +	 * Callback of the each instance of LLSpeakerActionTimer. +	 * +	 * Unsets an appropriate timer instance and calls action callback for specified speacker_id. +	 * It always returns false to not use LLEventTimer::updateClass functionality of timer deleting. +	 * +	 * @see unsetActionTimer() +	 */ +	bool onTimerActionCallback(const LLUUID& speaker_id); + +	LLSpeakerActionTimer::action_timers_map_t	mActionTimersMap; +	LLSpeakerActionTimer::action_callback_t		mActionCallback; + +	/** +	 * Delay to call action callback for speakers after timer was set. +	 */ +	F32	mActionDelay; + +}; + +  class LLSpeakerMgr : public LLOldEvents::LLObservable  {  public: @@ -144,6 +229,8 @@ public:  protected:  	virtual void updateSpeakerList(); +	void setSpeakerNotInChannel(LLSpeaker* speackerp); +	bool removeSpeaker(const LLUUID& speaker_id);  	typedef std::map<LLUUID, LLPointer<LLSpeaker> > speaker_map_t;  	speaker_map_t		mSpeakers; @@ -151,6 +238,11 @@ protected:  	speaker_list_t		mSpeakersSorted;  	LLFrameTimer		mSpeechTimer;  	LLVoiceChannel*		mVoiceChannel; + +	/** +	 * time out speakers when they are not part of current session +	 */ +	LLSpeakersDelayActionsStorage* mSpeakerDelayRemover;  };  class LLIMSpeakerMgr : public LLSpeakerMgr diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 9c4825763b..2b846d33fc 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1103,7 +1103,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)  	{  		if (op == TEXTURE_CANCEL)  			mViewModel->resetDirty(); -		else +		// If the "no_commit_on_selection" parameter is set +		// we get dirty only when user presses OK in the picker +		// (i.e. op == TEXTURE_SELECT) or texture changes via DnD. +		else if (mCommitOnSelection || op == TEXTURE_SELECT)  			mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here?  		if( floaterp->isDirty() ) @@ -1125,7 +1128,7 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)  			{  				// If the "no_commit_on_selection" parameter is set  				// we commit only when user presses OK in the picker -				// (i.e. op == TEXTURE_SELECT) or changes texture via DnD. +				// (i.e. op == TEXTURE_SELECT) or texture changes via DnD.  				if (mCommitOnSelection || op == TEXTURE_SELECT)  					onCommit();  			} @@ -1165,6 +1168,9 @@ BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,  		{  			if(doDrop(item))  			{ +				if (!mCommitOnSelection) +					mViewModel->setDirty(); +  				// This removes the 'Multiple' overlay, since  				// there is now only one texture selected.  				setTentative( FALSE );  diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 023329a9b2..8ca92c3d87 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -74,7 +74,8 @@ public:  		Optional<std::string>	default_image_name;  		Optional<bool>			allow_no_texture;  		Optional<bool>			can_apply_immediately; -		Optional<bool>			no_commit_on_selection; // don't commit unless it's DnD or OK button press +		Optional<bool>			no_commit_on_selection; // alternative mode: commit occurs and the widget gets dirty +														// only on DnD or when OK is pressed in the picker  		Optional<S32>			label_width;  		Optional<LLUIColor>		border_color; diff --git a/indra/newview/lltransientdockablefloater.cpp b/indra/newview/lltransientdockablefloater.cpp index 7e4d4988d1..c9bfe178ce 100644 --- a/indra/newview/lltransientdockablefloater.cpp +++ b/indra/newview/lltransientdockablefloater.cpp @@ -42,6 +42,7 @@ LLTransientDockableFloater::LLTransientDockableFloater(LLDockControl* dockContro  		LLDockableFloater(dockControl, uniqueDocking, key, params)  {  	LLTransientFloaterMgr::getInstance()->registerTransientFloater(this); +	LLTransientFloater::init(this);  }  LLTransientDockableFloater::~LLTransientDockableFloater() diff --git a/indra/newview/lltransientdockablefloater.h b/indra/newview/lltransientdockablefloater.h index 6e8a3afd22..e0541d6597 100644 --- a/indra/newview/lltransientdockablefloater.h +++ b/indra/newview/lltransientdockablefloater.h @@ -37,12 +37,13 @@  #include "llfloater.h"  #include "lldockcontrol.h"  #include "lldockablefloater.h" +#include "lltransientfloatermgr.h"  /**   * Represents floater that can dock and managed by transient floater manager.   * Transient floaters should be hidden if user click anywhere except defined view list.   */ -class LLTransientDockableFloater : public LLDockableFloater +class LLTransientDockableFloater : public LLDockableFloater, LLTransientFloater  {  public:  	LOG_CLASS(LLTransientDockableFloater); @@ -52,6 +53,7 @@ public:  	/*virtual*/ void setVisible(BOOL visible);  	/* virtual */void setDocked(bool docked, bool pop_on_undock = true); +	virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; }  };  #endif /* LL_TRANSIENTDOCKABLEFLOATER_H */ diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp index 347399f239..8f1a738453 100644 --- a/indra/newview/lltransientfloatermgr.cpp +++ b/indra/newview/lltransientfloatermgr.cpp @@ -44,57 +44,68 @@ LLTransientFloaterMgr::LLTransientFloaterMgr()  {  	gViewerWindow->getRootView()->addMouseDownCallback(boost::bind(  			&LLTransientFloaterMgr::leftMouseClickCallback, this, _1, _2, _3)); + +	mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(GLOBAL, std::set<LLView*>())); +	mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(IM, std::set<LLView*>()));  } -void LLTransientFloaterMgr::registerTransientFloater(LLFloater* floater) +void LLTransientFloaterMgr::registerTransientFloater(LLTransientFloater* floater)  {  	mTransSet.insert(floater);  } -void LLTransientFloaterMgr::unregisterTransientFloater(LLFloater* floater) +void LLTransientFloaterMgr::unregisterTransientFloater(LLTransientFloater* floater)  {  	mTransSet.erase(floater);  } +void LLTransientFloaterMgr::addControlView(ETransientGroup group, LLView* view) +{ +	mGroupControls.find(group)->second.insert(view); +} + +void LLTransientFloaterMgr::removeControlView(ETransientGroup group, LLView* view) +{ +	mGroupControls.find(group)->second.erase(view); +} +  void LLTransientFloaterMgr::addControlView(LLView* view)  { -	mControlsSet.insert(view); +	addControlView(GLOBAL, view);  }  void LLTransientFloaterMgr::removeControlView(LLView* view)  {  	// we will still get focus lost callbacks on this view, but that's ok  	// since we run sanity checking logic every time -	mControlsSet.erase(view); +	removeControlView(GLOBAL, view);  } -void LLTransientFloaterMgr::hideTransientFloaters() +void LLTransientFloaterMgr::hideTransientFloaters(S32 x, S32 y)  { -	for (std::set<LLFloater*>::iterator it = mTransSet.begin(); it +	for (std::set<LLTransientFloater*>::iterator it = mTransSet.begin(); it  			!= mTransSet.end(); it++)  	{ -		LLFloater* floater = *it; -		if (floater->isDocked()) +		LLTransientFloater* floater = *it; +		if (floater->isTransientDocked())  		{ -			floater->setVisible(FALSE); +			ETransientGroup group = floater->getGroup(); + +			bool hide = isControlClicked(mGroupControls.find(group)->second, x, y); +			if (hide) +			{ +				floater->setTransientVisible(FALSE); +			}  		}  	}  } -void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y, -		MASK mask) +bool LLTransientFloaterMgr::isControlClicked(std::set<LLView*>& set, S32 x, S32 y)  { -	bool hide = true; -	for (controls_set_t::iterator it = mControlsSet.begin(); it -			!= mControlsSet.end(); it++) +	bool res = true; +	for (controls_set_t::iterator it = set.begin(); it +			!= set.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())  		{ @@ -105,14 +116,32 @@ void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y,  		// if click inside view rect  		if (rect.pointInRect(x, y))  		{ -			hide = false; +			res = false;  			break;  		}  	} +	return res; +} + +void LLTransientFloaterMgr::leftMouseClickCallback(S32 x, S32 y, +		MASK mask) +{ +	// don't hide transient floater if any context menu opened +	if (LLMenuGL::sMenuContainer->getVisibleMenu() != NULL) +	{ +		return; +	} +	bool hide = isControlClicked(mGroupControls.find(GLOBAL)->second, x, y);  	if (hide)  	{ -		hideTransientFloaters(); +		hideTransientFloaters(x, y);  	}  } +void LLTransientFloater::init(LLFloater* thiz) +{ +	// used since LLTransientFloater(this) can't be used in descendant constructor parameter initialization. +	mFloater = thiz; +} + diff --git a/indra/newview/lltransientfloatermgr.h b/indra/newview/lltransientfloatermgr.h index cef6e1fe45..1f99325a7f 100644 --- a/indra/newview/lltransientfloatermgr.h +++ b/indra/newview/lltransientfloatermgr.h @@ -37,27 +37,60 @@  #include "llsingleton.h"  #include "llfloater.h" +class LLTransientFloater;  /**   * Provides functionality to hide transient floaters.   */  class LLTransientFloaterMgr: public LLSingleton<LLTransientFloaterMgr>  { -public: +protected:  	LLTransientFloaterMgr(); -	void registerTransientFloater(LLFloater* floater); -	void unregisterTransientFloater(LLFloater* floater); +	friend class LLSingleton<LLTransientFloaterMgr>; + +public: +	enum ETransientGroup +	{ +		GLOBAL, IM +	}; + +	void registerTransientFloater(LLTransientFloater* floater); +	void unregisterTransientFloater(LLTransientFloater* floater); +	void addControlView(ETransientGroup group, LLView* view); +	void removeControlView(ETransientGroup group, LLView* view);  	void addControlView(LLView* view);  	void removeControlView(LLView* view);  private: -	void hideTransientFloaters(); +	void hideTransientFloaters(S32 x, S32 y);  	void leftMouseClickCallback(S32 x, S32 y, MASK mask); - +	bool isControlClicked(std::set<LLView*>& set, S32 x, S32 y);  private: -	std::set<LLFloater*> mTransSet; +	std::set<LLTransientFloater*> mTransSet; +  	typedef std::set<LLView*> controls_set_t; -	controls_set_t mControlsSet; +	typedef std::map<ETransientGroup, std::set<LLView*> > group_controls_t; +	group_controls_t mGroupControls; +}; + +/** + * An abstract class declares transient floater interfaces. + */ +class LLTransientFloater +{ +protected: +	/** +	 * Class initialization method. +	 * Should be called from descendant constructor. +	 */ +	void init(LLFloater* thiz); +public: +	virtual LLTransientFloaterMgr::ETransientGroup getGroup() = 0; +	bool isTransientDocked() { return mFloater->isDocked(); }; +	void setTransientVisible(BOOL visible) {mFloater->setVisible(visible); } + +private: +	LLFloater* mFloater;  };  #endif  // LL_LLTRANSIENTFLOATERMGR_H diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 7487fa9997..0358efc0af 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1472,7 +1472,12 @@ void inventory_offer_handler(LLOfferInfo* info)  	{  		LLStringUtil::truncate(msg, indx);  	} -	 + +	if(LLAssetType::AT_LANDMARK == info->mType) +	{ +		msg = LLViewerInventoryItem::getDisplayName(msg); +	} +  	LLSD args;  	args["[OBJECTNAME]"] = msg; diff --git a/indra/newview/skins/default/xui/en/favorites_bar_button.xml b/indra/newview/skins/default/xui/en/favorites_bar_button.xml index 6adf2a5950..b365040c20 100644 --- a/indra/newview/skins/default/xui/en/favorites_bar_button.xml +++ b/indra/newview/skins/default/xui/en/favorites_bar_button.xml @@ -10,6 +10,8 @@   image_selected="transparent.j2c"   image_unselected="transparent.j2c"   image_pressed="Favorite_Link_Over" + image_hover_selected="Favorite_Link_Over" + image_hover_unselected="Favorite_Link_Over"   hover_glow_amount="0.15"   label_shadow="false"   layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index 1d51d19a4a..bd25288a9e 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -1,6 +1,7 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <multi_floater - can_minimize="false" + can_close="false"   + can_minimize="true"   can_resize="true"   height="390"   layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index d3db9fd531..09ec2137b7 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -416,7 +416,6 @@ image_pressed_selected  "Lit" + "Selected" - there are new messages and the Well                 name="Unread"                 image_overlay="Notices_Unread"                 image_overlay_alignment="center" -               pad_right="15"                 tool_tip="Notifications"                 width="34" >                    <button.init_callback diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml index 673052c3b5..0893c204e7 100644 --- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml @@ -67,6 +67,7 @@ background_visible="true"       layout="topleft"       left="20"       name="insignia" +     no_commit_on_selection="true"       tool_tip="Click to choose a picture"       top_pad="5"       width="100" /> diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml index 6b3fb04549..618d2f3b8e 100644 --- a/indra/newview/skins/default/xui/en/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml @@ -49,6 +49,18 @@  Select multiple Members by holding the Ctrl key and  clicking on their names.              </panel.string> +            <panel.string +             name="power_folder_icon"> +                Inv_FolderClosed +            </panel.string> +            <panel.string +             name="power_all_have_icon"> +                Checkbox_On +            </panel.string> +            <panel.string +             name="power_partial_icon"> +                Checkbox_Off +            </panel.string>           <filter_editor           layout="topleft"           top="5" @@ -310,7 +322,7 @@ things in this group. There's a broad variety of Abilities.           width="285">              Allowed Abilities          </text> -         <scroll_list +        <scroll_list           draw_stripes="true"           height="90"           layout="topleft" @@ -322,8 +334,16 @@ things in this group. There's a broad variety of Abilities.           width="300">              <scroll_list.columns               label="" +             name="icon" +             width="2" /> +            <scroll_list.columns +             label="" +             name="checkbox" +             width="20" /> +            <scroll_list.columns +             label=""               name="action" -             width="300" /> +             width="270" />          </scroll_list>      </panel>      <panel diff --git a/indra/newview/skins/default/xui/en/panel_my_profile.xml b/indra/newview/skins/default/xui/en/panel_my_profile.xml index 34cde61252..4894ae01da 100644 --- a/indra/newview/skins/default/xui/en/panel_my_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_my_profile.xml @@ -1,7 +1,7 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <panel   follows="all" - height="535" + height="500"   label="Profile"   layout="topleft"   left="0" @@ -41,7 +41,7 @@       layout="topleft"       left="0"       top="0" -     height="535" +     height="480"       width="313"       border_size="0">        <layout_panel @@ -50,7 +50,7 @@           layout="topleft"           top="0"           left="0" -         height="505" +         height="480"           user_resize="false"           width="313">          <scroll_container @@ -60,7 +60,7 @@           left="0"           name="profile_scroll"           opaque="true" -         height="505" +         height="480"           width="313"           top="0">            <panel @@ -374,8 +374,9 @@           follows="bottom|right"           height="23"           left="20" -	 top="0" +	 top="5"           label="Edit Profile" +         layout="topleft"           name="edit_profile_btn"           tool_tip="Edit your personal information"           width="130" /> @@ -384,7 +385,9 @@           height="23"           label="Edit Appearance"           left_pad="10" +         layout="topleft"           name="edit_appearance_btn" +         top="5"           tool_tip="Create/edit your appearance: physical data, clothes and etc."           width="130" />   </layout_panel> | 
