diff options
38 files changed, 1488 insertions, 876 deletions
| diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index dae4b512d1..219fae84be 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -512,50 +512,6 @@ std::string LLUrlEntryTeleport::getLocation(const std::string &url) const  	return ::getStringAfterToken(url, "app/teleport/");  } -/// -/// LLUrlEntryObjectIM Describes a Second Life object instant msg Url, e.g., -/// secondlife:///app/objectim/<sessionid> -/// -LLUrlEntryObjectIM::LLUrlEntryObjectIM() -{ -	mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\\??\\S*", -							boost::regex::perl|boost::regex::icase); -	mMenuName = "menu_url_objectim.xml"; -	mTooltip = LLTrans::getString("TooltipObjectIMUrl"); -} - -std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ -	LLURI uri(url); -	LLSD params = uri.queryMap(); -	if (params.has("name")) -	{ -		// look for a ?name=<obj-name> param in the url -		// and use that as the label if present. -		std::string name = params.get("name"); -		LLStringUtil::trim(name); -		if (name.empty()) -		{ -			name = LLTrans::getString("Unnamed"); -		} -		return name; -	} - -	return unescapeUrl(url); -} - -std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const -{ -	LLURI uri(url); -	LLSD params = uri.queryMap(); -	if (params.has("slurl")) -	{ -		return params.get("slurl"); -	} - -	return ""; -} -  //  // LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts  // with secondlife:// (used as a catch-all for cases not matched above) diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 4507572b1e..7970b48eb5 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -209,18 +209,6 @@ public:  };  /// -/// LLUrlEntryObjectIM Describes a Second Life object instant msg Url, e.g., -/// secondlife:///app/objectim/<sessionid>?name=Foo -/// -class LLUrlEntryObjectIM : public LLUrlEntryBase -{ -public: -	LLUrlEntryObjectIM(); -	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); -	/*virtual*/ std::string getLocation(const std::string &url) const; -}; - -///  /// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts  /// with secondlife:// (used as a catch-all for cases not matched above)  /// diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 60275b60bc..a6922b019b 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -52,7 +52,6 @@ LLUrlRegistry::LLUrlRegistry()  	registerUrl(new LLUrlEntryParcel());  	registerUrl(new LLUrlEntryTeleport());  	registerUrl(new LLUrlEntryWorldMap()); -	registerUrl(new LLUrlEntryObjectIM());  	registerUrl(new LLUrlEntryPlace());  	registerUrl(new LLUrlEntrySL());  	registerUrl(new LLUrlEntrySLLabel()); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e69d3f417f..9d44f34ea8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -246,11 +246,13 @@ set(viewer_SOURCE_FILES      llinspectavatar.cpp      llinspectgroup.cpp      llinspectobject.cpp +    llinspectremoteobject.cpp      llinventorybridge.cpp      llinventoryclipboard.cpp      llinventoryfilter.cpp      llinventoryfunctions.cpp      llinventorymodel.cpp +    llinventoryobserver.cpp      llinventorypanel.cpp      llinventorysubtreepanel.cpp      lljoystickbutton.cpp @@ -741,11 +743,13 @@ set(viewer_HEADER_FILES      llinspectavatar.h      llinspectgroup.h      llinspectobject.h +    llinspectremoteobject.h      llinventorybridge.h      llinventoryclipboard.h      llinventoryfilter.h      llinventoryfunctions.h      llinventorymodel.h +    llinventoryobserver.h      llinventorypanel.h      llinventorysubtreepanel.h      lljoystickbutton.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 15fa057230..eb4148f92f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4259,7 +4259,29 @@        <key>Value</key>        <integer>0</integer>      </map> -    <key>LogMessages</key> +  <key>LoginSRVTimeout</key> +  <map> +    <key>Comment</key> +    <string>Duration in seconds of the login SRV request timeout</string> +    <key>Persist</key> +    <integer>0</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>10.0</real> +  </map> +  <key>LoginSRVPump</key> +  <map> +    <key>Comment</key> +    <string>Name of the message pump that handles SRV request</string> +    <key>Persist</key> +    <integer>0</integer> +    <key>Type</key> +    <string>String</string> +    <key>Value</key> +    <string>LLAres</string> +  </map> +  <key>LogMessages</key>      <map>        <key>Comment</key>        <string>Log network traffic</string> diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 3da6a4e3f4..b3ed7c353e 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -48,7 +48,7 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const  		params.append(event_data["y"]);  		params.append(event_data["z"]);  		LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, true); -		// *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "objectim", "parcel", "login", login_refresh", "balance", "chat" +		// *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat"  		// should we just compose LLCommandHandler and LLDispatchListener?  	}  	else diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 9938c3db2b..9b4986247f 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -887,9 +887,8 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs  			lldebugs << "       " << LLWearableDictionary::getTypeLabel(type) << llendl;  		} -		// What we do here is get the complete information on the items in -		// the inventory, and set up an observer that will wait for that to -		// happen. +		// Get the complete information on the items in the inventory and set up an observer +		// that will trigger when the complete information is fetched.  		LLInventoryFetchDescendentsObserver::folder_ref_t folders;  		folders.push_back(current_outfit_id);  		outfit->fetchDescendents(folders); @@ -2023,6 +2022,8 @@ void LLInitialWearablesFetch::done()  	else  	{  		processWearablesMessage(); +		// Create links for attachments that may have arrived before the COF existed. +		LLAppearanceManager::linkRegisteredAttachments();  	}  	delete this;  } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index d14de1c301..0901289dac 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1123,6 +1123,7 @@ void LLAppearanceManager::wearItem( LLInventoryItem* item, bool do_update )  	{  		if (do_update)  			LLAppearanceManager::updateAppearanceFromCOF(); +		return;  	}  	else  	{ @@ -1134,6 +1135,7 @@ void LLAppearanceManager::wearItem( LLInventoryItem* item, bool do_update )  							 LLAssetType::AT_LINK,  							 cb);  	} +	return;  }  /* static */ @@ -1281,3 +1283,22 @@ void LLAppearanceManager::unregisterAttachment(const LLUUID& item_id)  		   //llinfos << "no link changes, inv link not enabled" << llendl;  	   }  } + +/* static */ +void LLAppearanceManager::linkRegisteredAttachments() +{ +	for (std::set<LLUUID>::iterator it = sRegisteredAttachments.begin(); +		 it != sRegisteredAttachments.end(); +		 ++it) +	{ +		LLUUID item_id = *it; +		LLViewerInventoryItem *item = gInventory.getItem(item_id); +		if (item) +		{ +			wearItem(item, false); +			gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); +			gInventory.notifyObservers(); +		} +	} +	sRegisteredAttachments.clear(); +} diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 56f54dfc23..7dea16b6cf 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -71,6 +71,7 @@ public:  	static void unregisterAttachment(const LLUUID& item_id);  	static void registerAttachment(const LLUUID& item_id);  	static void setAttachmentInvLinkEnable(bool val); +	static void linkRegisteredAttachments();  private:  	static void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type); diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 204d7d23fa..ab685b69ad 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -40,6 +40,7 @@  #include "llimfloater.h" // for LLIMFloater  #include "lllayoutstack.h"  #include "llnearbychatbar.h" +#include "llspeakbutton.h"  #include "llsplitbutton.h"  #include "llsyswellwindow.h"  #include "llfloatercamera.h" @@ -185,6 +186,28 @@ void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID&  	}  } +// virtual +void LLBottomTray::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ +	// Time it takes to connect to voice channel might be pretty long, +	// so don't expect user login or STATUS_VOICE_ENABLED to be followed by STATUS_JOINED. +	BOOL enable = FALSE; + +	switch (status) +	{ +	// Do not add STATUS_VOICE_ENABLED because voice chat is  +	// inactive until STATUS_JOINED +	case STATUS_JOINED: +		enable = TRUE; +		break; +	default: +		enable = FALSE; +		break; +	} + +	mSpeakBtn->setEnabled(enable); +} +  //virtual  void LLBottomTray::onFocusLost()  { @@ -280,6 +303,19 @@ BOOL LLBottomTray::postBuild()  	mSnapshotPanel = getChild<LLPanel>("snapshot_panel");  	setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4)); +	mSpeakBtn = getChild<LLSpeakButton>("talk"); + +	// Speak button should be initially disabled because +	// it takes some time between logging in to world and connecting to voice channel. +	mSpeakBtn->setEnabled(FALSE); + +	// Localization tool doesn't understand custom buttons like <talk_button> +	mSpeakBtn->setSpeakToolTip( getString("SpeakBtnToolTip") ); +	mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") ); + +	// Registering Chat Bar to receive Voice client status change notifications. +	gVoiceClient->addObserver(this); +  	if (mChicletPanel && mToolbarStack && mNearbyChatBar)  	{  		verifyChildControlsSizes(); diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 6509fea63d..c88bdeda1c 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -33,7 +33,7 @@  #ifndef LL_LLBOTTOMPANEL_H  #define LL_LLBOTTOMPANEL_H -#include <llmenugl.h> +#include "llmenugl.h"  #include "llpanel.h"  #include "llimview.h" @@ -51,6 +51,7 @@ class LLBottomTray  	: public LLSingleton<LLBottomTray>  	, public LLPanel  	, public LLIMSessionObserver +	, public LLVoiceClientStatusObserver  {  	LOG_CLASS(LLBottomTray);  	friend class LLSingleton<LLBottomTray>; @@ -75,6 +76,10 @@ public:  	virtual void onFocusLost();  	virtual void setVisible(BOOL visible); +	// Implements LLVoiceClientStatusObserver::onChange() to enable the speak +	// button when voice is available +	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); +  	void showBottomTrayContextMenu(S32 x, S32 y, MASK mask);  	void showGestureButton(BOOL visible); diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index ca2cdffcf8..e2df2ffdf7 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -124,9 +124,19 @@ void LLFloaterSearch::search(const LLSD &key)  	url += "&p=" + search_token.asString();  	// also append the user's preferred maturity (can be changed via prefs) -	std::string maturity = "pg"; -	if (gAgent.prefersMature()) maturity += ",mature"; -	if (gAgent.prefersAdult()) maturity += ",adult"; +	std::string maturity; +	if (gAgent.prefersAdult()) +	{ +		maturity = "42";  // PG,Mature,Adult +	} +	else if (gAgent.prefersMature()) +	{ +		maturity = "21";  // PG,Mature +	} +	else +	{ +		maturity = "13";  // PG +	}  	url += "&r=" + maturity;  	// and load the URL in the web view diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp new file mode 100644 index 0000000000..e4d2eec242 --- /dev/null +++ b/indra/newview/llinspectremoteobject.cpp @@ -0,0 +1,200 @@ +/**  + * @file llinspectremoteobject.cpp + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinspectremoteobject.h" +#include "llinspect.h" +#include "llslurl.h" +#include "llmutelist.h" +#include "llurlaction.h" +#include "llpanelblockedlist.h" +#include "llfloaterreg.h" +#include "llui.h" +#include "lluictrl.h" + +class LLViewerObject; + +////////////////////////////////////////////////////////////////////////////// +// LLInspectRemoteObject +////////////////////////////////////////////////////////////////////////////// + +// Remote Object Inspector, a small information window used to +// display information about potentially-remote objects. Used +// to display details about objects sending messages to the user. +class LLInspectRemoteObject : public LLInspect +{ +	friend class LLFloaterReg; +	 +public: +	LLInspectRemoteObject(const LLSD& object_id); +	virtual ~LLInspectRemoteObject() {}; + +	/*virtual*/ BOOL postBuild(void); +	/*virtual*/ void onOpen(const LLSD& avatar_id); + +	void onClickMap(); +	void onClickBlock(); +	void onClickClose(); +	 +private: +	void update(); +	static void nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data); +	 +private: +	LLUUID		 mObjectID; +	LLUUID		 mOwnerID; +	std::string  mOwner; +	std::string  mSLurl; +	std::string  mName; +	bool         mGroupOwned; +}; + +LLInspectRemoteObject::LLInspectRemoteObject(const LLSD& sd) : +	LLInspect(LLSD()), +	mObjectID(NULL), +	mOwnerID(NULL), +	mOwner(""), +	mSLurl(""), +	mName(""), +	mGroupOwned(false) +{ +} + +/*virtual*/ +BOOL LLInspectRemoteObject::postBuild(void) +{ +	// hook up the inspector's buttons +	getChild<LLUICtrl>("map_btn")->setCommitCallback( +		boost::bind(&LLInspectRemoteObject::onClickMap, this)); +	getChild<LLUICtrl>("block_btn")->setCommitCallback( +		boost::bind(&LLInspectRemoteObject::onClickBlock, this)); +	getChild<LLUICtrl>("close_btn")->setCommitCallback( +		boost::bind(&LLInspectRemoteObject::onClickClose, this)); + +	return TRUE; +} + +/*virtual*/ +void LLInspectRemoteObject::onOpen(const LLSD& data) +{ +	// Start animation +	LLInspect::onOpen(data); + +	// Extract appropriate object information from input LLSD +	// (Eventually, it might be nice to query server for details +	// rather than require caller to pass in the information.) +	mObjectID   = data["object_id"].asUUID(); +	mName       = data["name"].asString(); +	mOwnerID    = data["owner_id"].asUUID(); +	mGroupOwned = data["group_owned"].asBoolean(); +	mSLurl      = data["slurl"].asString(); + +	// work out the owner's name +	mOwner = ""; +	if (gCacheName) +	{ +		gCacheName->get(mOwnerID, mGroupOwned, nameCallback, this); +	} + +	// update the inspector with the current object state +	update(); + +	// Position the inspector relative to the mouse cursor +	LLUI::positionViewNearMouse(this); +} + +void LLInspectRemoteObject::onClickMap() +{ +	std::string url = "secondlife://" + mSLurl; +	LLUrlAction::showLocationOnMap(url); +	closeFloater(); +} + +void LLInspectRemoteObject::onClickBlock() +{ +	LLMute::EType mute_type = mGroupOwned ? LLMute::GROUP : LLMute::AGENT; +	LLMute mute(mOwnerID, mOwner, mute_type); +	LLMuteList::getInstance()->add(mute); +	LLPanelBlockedList::showPanelAndSelect(mute.mID); +	closeFloater(); +} + +void LLInspectRemoteObject::onClickClose() +{ +	closeFloater(); +} + +//static  +void LLInspectRemoteObject::nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data) +{ +	LLInspectRemoteObject *self = (LLInspectRemoteObject*)data; +	self->mOwner = first; +	if (!last.empty()) +	{ +		self->mOwner += " " + last; +	} +	self->update(); +} + +void LLInspectRemoteObject::update() +{ +	// show the object name as the inspector's title +	getChild<LLUICtrl>("object_name")->setValue(mName); + +	// show the object's owner - click it to show profile +	std::string owner = mOwner; +	if (! mOwnerID.isNull()) +	{ +		if (mGroupOwned) +		{ +			owner = LLSLURL::buildCommand("group", mOwnerID, "about"); +		} +		else +		{ +			owner = LLSLURL::buildCommand("agent", mOwnerID, "about"); +		} +	} +	getChild<LLUICtrl>("object_owner")->setValue(owner); + +	// display the object's SLurl - click it to teleport +	std::string url = "secondlife:///app/teleport/" + mSLurl; +	getChild<LLUICtrl>("object_slurl")->setValue(url); +} + +////////////////////////////////////////////////////////////////////////////// +// LLInspectRemoteObjectUtil +////////////////////////////////////////////////////////////////////////////// +void LLInspectRemoteObjectUtil::registerFloater() +{ +	LLFloaterReg::add("inspect_remote_object", "inspect_remote_object.xml", +					  &LLFloaterReg::build<LLInspectRemoteObject>); +} diff --git a/indra/newview/llinspectremoteobject.h b/indra/newview/llinspectremoteobject.h new file mode 100644 index 0000000000..e756f1caf4 --- /dev/null +++ b/indra/newview/llinspectremoteobject.h @@ -0,0 +1,40 @@ +/**  + * @file llinspectremoteobject.h + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLINSPECTREMOTEOBJECT_H +#define LLINSPECTREMOTEOBJECT_H + +namespace LLInspectRemoteObjectUtil +{ +	void registerFloater(); +} + +#endif diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index e7d7eb19d0..4b0d524906 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -176,6 +176,7 @@ LLInventoryModel::LLInventoryModel()  	mRootFolderID(),  	mLibraryRootFolderID(),  	mLibraryOwnerID(), +	mIsNotifyObservers(FALSE),  	mIsAgentInvUsable(false)  {  } @@ -537,7 +538,10 @@ void LLInventoryModel::updateLinkedItems(const LLUUID& object_id)  						 item_array,  						 LLInventoryModel::INCLUDE_TRASH,  						 is_linked_item_match); - +	if (cat_array.empty() && item_array.empty()) +	{ +		return; +	}  	for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin();  		 cat_iter != cat_array.end();  		 cat_iter++) @@ -639,6 +643,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)  		new_item = old_item;  		LLUUID old_parent_id = old_item->getParentUUID();  		LLUUID new_parent_id = item->getParentUUID(); +			  		if(old_parent_id != new_parent_id)  		{  			// need to update the parent-child tree @@ -1133,6 +1138,15 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const  // The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328]  void LLInventoryModel::notifyObservers(const std::string service_name)  { +	if (mIsNotifyObservers) +	{ +		// Within notifyObservers, something called notifyObservers +		// again.  This type of recursion is unsafe because it causes items to be  +		// processed twice, and this can easily lead to infinite loops. +		llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl; +		return; +	} +	mIsNotifyObservers = TRUE;  	for (observer_list_t::iterator iter = mObservers.begin();  		 iter != mObservers.end(); )  	{ @@ -1154,12 +1168,21 @@ void LLInventoryModel::notifyObservers(const std::string service_name)  	mModifyMask = LLInventoryObserver::NONE;  	mChangedItemIDs.clear(); +	mIsNotifyObservers = FALSE;  }  // store flag for change  // and id of object change applies to  void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)   {  +	if (mIsNotifyObservers) +	{ +		// Something marked an item for change within a call to notifyObservers +		// (which is in the process of processing the list of items marked for change). +		// This means the change may fail to be processed. +		llwarns << "Adding changed mask within notify observers!  Change will likely be lost." << llendl; +	} +	  	mModifyMask |= mask;   	if (referent.notNull())  	{ @@ -1833,13 +1856,13 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)  {  	//llinfos << "LLInventoryModel::addItem()" << llendl; -	 -	// This can happen if assettype enums change.  This can be a backwards compatibility issue  -	// in some viewer prototypes prior to when the AT_LINK enum changed from 23 to 24. +	// This can happen if assettype enums from llassettype.h ever change. +	// For example, there is a known backwards compatibility issue in some viewer prototypes prior to when  +	// the AT_LINK enum changed from 23 to 24.  	if ((item->getType() == LLAssetType::AT_NONE)  		|| LLAssetType::lookup(item->getType()) == LLAssetType::badLookup())  	{ -		llwarns << "Got bad asset type for item ( name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ), ignoring." << llendl; +		llwarns << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << llendl;  		return;  	}  	if(item) @@ -1848,7 +1871,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)  		// The item will show up as a broken link.  		if (item->getIsBrokenLink())  		{ -			llinfos << "Adding broken link ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " )  parent: " << item->getParentUUID() << llendl; +			llinfos << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " )  parent: " << item->getParentUUID() << llendl;  		}  		mItemMap[item->getUUID()] = item;  	} @@ -2176,7 +2199,7 @@ bool LLInventoryModel::loadSkeleton(  			// Add all the items loaded which are parented to a  			// category with a correctly cached parent -			count = items.count(); +			S32 bad_link_count = 0;  			cat_map_t::iterator unparented = mCategoryMap.end();  			for(item_array_t::const_iterator item_iter = items.begin();  				item_iter != items.end(); @@ -2193,7 +2216,11 @@ bool LLInventoryModel::loadSkeleton(  						// This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache.  						if (item->getIsBrokenLink())  						{ -							llinfos << "Attempted to add cached link item without baseobj present ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ).  Ignoring and invalidating " << cat->getName() << " . " << llendl; +							bad_link_count++; +							lldebugs << "Attempted to add cached link item without baseobj present ( name: " +									 << item->getName() << " itemID: " << item->getUUID() +									 << " assetID: " << item->getAssetUUID() +									 << " ).  Ignoring and invalidating " << cat->getName() << " . " << llendl;  							invalid_categories.insert(cit->second);  							continue;  						} @@ -2203,6 +2230,12 @@ bool LLInventoryModel::loadSkeleton(  					}  				}  			} +			if (bad_link_count > 0) +			{ +				llinfos << "Attempted to add " << bad_link_count +						<< " cached link items without baseobj present. " +						<< "The corresponding categories were invalidated." << llendl; +			}  		}  		else  		{ @@ -3307,6 +3340,12 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)  	for(i = 0; i < count; ++i)  	{  		titem->unpackMessage(msg, _PREHASH_ItemData, i); +		// If the item has already been added (e.g. from link prefetch), then it doesn't need to be re-added. +		if (gInventory.getItem(titem->getUUID())) +		{ +			llinfos << "Skipping prefetched item [ Name: " << titem->getName() << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << llendl; +			continue; +		}  		gInventory.updateItem(titem);  	} @@ -3682,513 +3721,6 @@ bool LLNameCategoryCollector::operator()(  	return false;  } - - -///---------------------------------------------------------------------------- -/// Observers -///---------------------------------------------------------------------------- - -void LLInventoryCompletionObserver::changed(U32 mask) -{ -	// scan through the incomplete items and move or erase them as -	// appropriate. -	if(!mIncomplete.empty()) -	{ -		for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) -		{ -			LLViewerInventoryItem* item = gInventory.getItem(*it); -			if(!item) -			{ -				it = mIncomplete.erase(it); -				continue; -			} -			if(item->isComplete()) -			{ -				mComplete.push_back(*it); -				it = mIncomplete.erase(it); -				continue; -			} -			++it; -		} -		if(mIncomplete.empty()) -		{ -			done(); -		} -	} -} - -void LLInventoryCompletionObserver::watchItem(const LLUUID& id) -{ -	if(id.notNull()) -	{ -		mIncomplete.push_back(id); -	} -} - - -void LLInventoryFetchObserver::changed(U32 mask) -{ -	// scan through the incomplete items and move or erase them as -	// appropriate. -	if(!mIncomplete.empty()) -	{ -		for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) -		{ -			LLViewerInventoryItem* item = gInventory.getItem(*it); -			if(!item) -			{ -				// BUG: This can cause done() to get called prematurely below. -				// This happens with the LLGestureInventoryFetchObserver that -				// loads gestures at startup. JC -				it = mIncomplete.erase(it); -				continue; -			} -			if(item->isComplete()) -			{ -				mComplete.push_back(*it); -				it = mIncomplete.erase(it); -				continue; -			} -			++it; -		} -		if(mIncomplete.empty()) -		{ -			done(); -		} -	} -	//llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl; -	//llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl; -} - -bool LLInventoryFetchObserver::isEverythingComplete() const -{ -	return mIncomplete.empty(); -} - -void fetch_items_from_llsd(const LLSD& items_llsd) -{ -	if (!items_llsd.size()) return; -	LLSD body; -	body[0]["cap_name"] = "FetchInventory"; -	body[1]["cap_name"] = "FetchLib"; -	for (S32 i=0; i<items_llsd.size();i++) -	{ -		if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString()) -		{ -			body[0]["items"].append(items_llsd[i]); -			continue; -		} -		if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString()) -		{ -			body[1]["items"].append(items_llsd[i]); -			continue; -		} -	} -		 -	for (S32 i=0; i<body.size(); i++) -	{ -		if (0 >= body[i].size()) continue; -		std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); - -		if (!url.empty()) -		{ -			body[i]["agent_id"]	= gAgent.getID(); -			LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); -			break; -		} - -		LLMessageSystem* msg = gMessageSystem; -		BOOL start_new_message = TRUE; -		for (S32 j=0; j<body[i]["items"].size(); j++) -		{ -			LLSD item_entry = body[i]["items"][j]; -			if(start_new_message) -			{ -				start_new_message = FALSE; -				msg->newMessageFast(_PREHASH_FetchInventory); -				msg->nextBlockFast(_PREHASH_AgentData); -				msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -				msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -			} -			msg->nextBlockFast(_PREHASH_InventoryData); -			msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID()); -			msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID()); -			if(msg->isSendFull(NULL)) -			{ -				start_new_message = TRUE; -				gAgent.sendReliableMessage(); -			} -		} -		if(!start_new_message) -		{ -			gAgent.sendReliableMessage(); -		} -	} -} - -void LLInventoryFetchObserver::fetchItems( -	const LLInventoryFetchObserver::item_ref_t& ids) -{ -	LLUUID owner_id; -	LLSD items_llsd; -	for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it) -	{ -		LLViewerInventoryItem* item = gInventory.getItem(*it); -		if(item) -		{ -			if(item->isComplete()) -			{ -				// It's complete, so put it on the complete container. -				mComplete.push_back(*it); -				continue; -			} -			else -			{ -				owner_id = item->getPermissions().getOwner(); -			} -		} -		else -		{ -			// assume it's agent inventory. -			owner_id = gAgent.getID(); -		} -		 -		// It's incomplete, so put it on the incomplete container, and -		// pack this on the message. -		mIncomplete.push_back(*it); -		 -		// Prepare the data to fetch -		LLSD item_entry; -		item_entry["owner_id"] = owner_id; -		item_entry["item_id"] = (*it); -		items_llsd.append(item_entry); -	} -	fetch_items_from_llsd(items_llsd); -} - -// virtual -void LLInventoryFetchDescendentsObserver::changed(U32 mask) -{ -	for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) -	{ -		LLViewerInventoryCategory* cat = gInventory.getCategory(*it); -		if(!cat) -		{ -			it = mIncompleteFolders.erase(it); -			continue; -		} -		if(isComplete(cat)) -		{ -			mCompleteFolders.push_back(*it); -			it = mIncompleteFolders.erase(it); -			continue; -		} -		++it; -	} -	if(mIncompleteFolders.empty()) -	{ -		done(); -	} -} - -void LLInventoryFetchDescendentsObserver::fetchDescendents( -	const folder_ref_t& ids) -{ -	for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it) -	{ -		LLViewerInventoryCategory* cat = gInventory.getCategory(*it); -		if(!cat) continue; -		if(!isComplete(cat)) -		{ -			cat->fetchDescendents();		//blindly fetch it without seeing if anything else is fetching it. -			mIncompleteFolders.push_back(*it);	//Add to list of things being downloaded for this observer. -		} -		else -		{ -			mCompleteFolders.push_back(*it); -		} -	} -} - -bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const -{ -	return mIncompleteFolders.empty(); -} - -bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat) -{ -	S32 version = cat->getVersion(); -	S32 descendents = cat->getDescendentCount(); -	if((LLViewerInventoryCategory::VERSION_UNKNOWN == version) -	   || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents)) -	{ -		return false; -	} -	// it might be complete - check known descendents against -	// currently available. -	LLInventoryModel::cat_array_t* cats; -	LLInventoryModel::item_array_t* items; -	gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); -	if(!cats || !items) -	{ -		// bit of a hack - pretend we're done if they are gone or -		// incomplete. should never know, but it would suck if this -		// kept tight looping because of a corrupt memory state. -		return true; -	} -	S32 known = cats->count() + items->count(); -	if(descendents == known) -	{ -		// hey - we're done. -		return true; -	} -	return false; -} - -void LLInventoryFetchComboObserver::changed(U32 mask) -{ -	if(!mIncompleteItems.empty()) -	{ -		for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); ) -		{ -			LLViewerInventoryItem* item = gInventory.getItem(*it); -			if(!item) -			{ -				it = mIncompleteItems.erase(it); -				continue; -			} -			if(item->isComplete()) -			{ -				mCompleteItems.push_back(*it); -				it = mIncompleteItems.erase(it); -				continue; -			} -			++it; -		} -	} -	if(!mIncompleteFolders.empty()) -	{ -		for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) -		{ -			LLViewerInventoryCategory* cat = gInventory.getCategory(*it); -			if(!cat) -			{ -				it = mIncompleteFolders.erase(it); -				continue; -			} -			if(gInventory.isCategoryComplete(*it)) -			{ -				mCompleteFolders.push_back(*it); -				it = mIncompleteFolders.erase(it); -				continue; -			} -			++it; -		} -	} -	if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty()) -	{ -		mDone = true; -		done(); -	} -} - -void LLInventoryFetchComboObserver::fetch( -	const folder_ref_t& folder_ids, -	const item_ref_t& item_ids) -{ -	lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl; -	for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit) -	{ -		LLViewerInventoryCategory* cat = gInventory.getCategory(*fit); -		if(!cat) continue; -		if(!gInventory.isCategoryComplete(*fit)) -		{ -			cat->fetchDescendents(); -			lldebugs << "fetching folder " << *fit <<llendl; -			mIncompleteFolders.push_back(*fit); -		} -		else -		{ -			mCompleteFolders.push_back(*fit); -			lldebugs << "completing folder " << *fit <<llendl; -		} -	} - -	// Now for the items - we fetch everything which is not a direct -	// descendent of an incomplete folder because the item will show -	// up in an inventory descendents message soon enough so we do not -	// have to fetch it individually. -	LLSD items_llsd; -	LLUUID owner_id; -	for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit) -	{ -		LLViewerInventoryItem* item = gInventory.getItem(*iit); -		if(!item) -		{ -			lldebugs << "uanble to find item " << *iit << llendl; -			continue; -		} -		if(item->isComplete()) -		{ -			// It's complete, so put it on the complete container. -			mCompleteItems.push_back(*iit); -			lldebugs << "completing item " << *iit << llendl; -			continue; -		} -		else -		{ -			mIncompleteItems.push_back(*iit); -			owner_id = item->getPermissions().getOwner(); -		} -		if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end()) -		{ -			LLSD item_entry; -			item_entry["owner_id"] = owner_id; -			item_entry["item_id"] = (*iit); -			items_llsd.append(item_entry); -		} -		else -		{ -			lldebugs << "not worrying about " << *iit << llendl; -		} -	} -	fetch_items_from_llsd(items_llsd); -} - -void LLInventoryExistenceObserver::watchItem(const LLUUID& id) -{ -	if(id.notNull()) -	{ -		mMIA.push_back(id); -	} -} - -void LLInventoryExistenceObserver::changed(U32 mask) -{ -	// scan through the incomplete items and move or erase them as -	// appropriate. -	if(!mMIA.empty()) -	{ -		for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); ) -		{ -			LLViewerInventoryItem* item = gInventory.getItem(*it); -			if(!item) -			{ -				++it; -				continue; -			} -			mExist.push_back(*it); -			it = mMIA.erase(it); -		} -		if(mMIA.empty()) -		{ -			done(); -		} -	} -} - -void LLInventoryAddedObserver::changed(U32 mask) -{ -	if(!(mask & LLInventoryObserver::ADD)) -	{ -		return; -	} - -	// *HACK: If this was in response to a packet off -	// the network, figure out which item was updated. -	LLMessageSystem* msg = gMessageSystem; - -	std::string msg_name; -	if (mMessageName.empty()) -	{ -		msg_name = msg->getMessageName(); -	} -	else -	{ -		msg_name = mMessageName; -	} - -	if (msg_name.empty()) -	{ -		return; -	} -	 -	// We only want newly created inventory items. JC -	if ( msg_name != "UpdateCreateInventoryItem") -	{ -		return; -	} - -	LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; -	S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); -	for(S32 i = 0; i < num_blocks; ++i) -	{ -		titem->unpackMessage(msg, _PREHASH_InventoryData, i); -		if (!(titem->getUUID().isNull())) -		{ -			//we don't do anything with null keys -			mAdded.push_back(titem->getUUID()); -		} -	} -	if (!mAdded.empty()) -	{ -		done(); -	} -} - -LLInventoryTransactionObserver::LLInventoryTransactionObserver( -	const LLTransactionID& transaction_id) : -	mTransactionID(transaction_id) -{ -} - -void LLInventoryTransactionObserver::changed(U32 mask) -{ -	if(mask & LLInventoryObserver::ADD) -	{ -		// This could be it - see if we are processing a bulk update -		LLMessageSystem* msg = gMessageSystem; -		if(msg->getMessageName() -		   && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory"))) -		{ -			// we have a match for the message - now check the -			// transaction id. -			LLUUID id; -			msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id); -			if(id == mTransactionID) -			{ -				// woo hoo, we found it -				folder_ref_t folders; -				item_ref_t items; -				S32 count; -				count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); -				S32 i; -				for(i = 0; i < count; ++i) -				{ -					msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i); -					if(id.notNull()) -					{ -						folders.push_back(id); -					} -				} -				count = msg->getNumberOfBlocksFast(_PREHASH_ItemData); -				for(i = 0; i < count; ++i) -				{ -					msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i); -					if(id.notNull()) -					{ -						items.push_back(id); -					} -				} - -				// call the derived class the implements this method. -				done(folders, items); -			} -		} -	} -} - -  ///----------------------------------------------------------------------------  /// LLAssetIDMatches   ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index faf026887a..bd64591194 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -41,40 +41,27 @@  #include "lluuid.h"  #include "llpermissionsflags.h"  #include "llstring.h" -  #include <map>  #include <set>  #include <string>  #include <vector> -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryObserver -// -// This class is designed to be a simple abstract base class which can -// relay messages when the inventory changes. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ! REFACTOR ! Remove llinventoryobservers.h and have other files that need it explicitly  +// include llinventoryobservers.h instead of llinventorymodel.h .  This will reduce dependency on +// llinventorymodel.h. +#include "llinventoryobserver.h"  + +class LLInventoryObserver; +class LLInventoryObject; +class LLInventoryItem; +class LLInventoryCategory; +class LLViewerInventoryItem; +class LLViewerInventoryCategory; +class LLViewerInventoryItem; +class LLViewerInventoryCategory; +class LLMessageSystem; +class LLInventoryCollectFunctor; -class LLInventoryObserver -{ -public: -	// This enumeration is a way to refer to what changed in a more -	// human readable format. You can mask the value provided by -	// chaged() to see if the observer is interested in the change. -	enum  -	{ -		NONE = 0, -		LABEL = 1,			// name changed -		INTERNAL = 2,		// internal change, eg, asset uuid different -		ADD = 4,			// something added -		REMOVE = 8,			// something deleted -		STRUCTURE = 16,		// structural change, eg, item or folder moved -		CALLING_CARD = 32,	// online, grant status, cancel, etc change -		ALL = 0xffffffff -	}; -	virtual ~LLInventoryObserver() {}; -	virtual void changed(U32 mask) = 0; -	std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328] -};  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLInventoryModel @@ -87,16 +74,6 @@ public:  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryObject; -class LLInventoryItem; -class LLInventoryCategory; -class LLViewerInventoryItem; -class LLViewerInventoryCategory; -class LLViewerInventoryItem; -class LLViewerInventoryCategory; -class LLMessageSystem; -class LLInventoryCollectFunctor; -  class LLInventoryModel  {  public: @@ -473,23 +450,12 @@ protected:  	cat_array_t* getUnlockedCatArray(const LLUUID& id);  	item_array_t* getUnlockedItemArray(const LLUUID& id); -protected: +private:  	// Variables used to track what has changed since the last notify.  	U32 mModifyMask;  	typedef std::set<LLUUID> changed_items_t;  	changed_items_t mChangedItemIDs; -	// Information for tracking the actual inventory. We index this -	// information in a lot of different ways so we can access -	// the inventory using several different identifiers. -	// mInventory member data is the 'master' list of inventory, and -	// mCategoryMap and mItemMap store uuid->object mappings.  -	typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t; -	typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t; -	//inv_map_t mInventory; -	cat_map_t mCategoryMap; -	item_map_t mItemMap; -  	std::map<LLUUID, bool> mCategoryLock;  	std::map<LLUUID, bool> mItemLock; @@ -525,6 +491,21 @@ protected:  	// This flag is used to handle an invalid inventory state.  	bool mIsAgentInvUsable; +private: +	// Information for tracking the actual inventory. We index this +	// information in a lot of different ways so we can access +	// the inventory using several different identifiers. +	// mInventory member data is the 'master' list of inventory, and +	// mCategoryMap and mItemMap store uuid->object mappings.  +	typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t; +	typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t; +	//inv_map_t mInventory; +	cat_map_t mCategoryMap; +	item_map_t mItemMap; + +	// Flag set when notifyObservers is being called, to look for bugs +	// where it's called recursively. +	BOOL mIsNotifyObservers;  public:  	// *NOTE: DEBUG functionality  	void dumpInventory() const; @@ -767,183 +748,5 @@ public:  							LLInventoryItem* item);  }; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryCompletionObserver -// -// Class which can be used as a base class for doing something when -// when all observed items are locally complete. This class implements -// the changed() method of LLInventoryObserver and declares a new -// method named done() which is called when all watched items have -// complete information in the inventory model. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryCompletionObserver : public LLInventoryObserver -{ -public: -	LLInventoryCompletionObserver() {} -	virtual void changed(U32 mask); - -	void watchItem(const LLUUID& id); - -protected: -	virtual void done() = 0; - -	typedef std::vector<LLUUID> item_ref_t; -	item_ref_t mComplete; -	item_ref_t mIncomplete; -}; - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryFetchObserver -// -// This class is much like the LLInventoryCompletionObserver, except -// that it handles all the the fetching necessary. Override the done() -// method to do the thing you want. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryFetchObserver : public LLInventoryObserver -{ -public: -	LLInventoryFetchObserver() {} -	virtual void changed(U32 mask); - -	typedef std::vector<LLUUID> item_ref_t; - -	bool isEverythingComplete() const; -	void fetchItems(const item_ref_t& ids); -	virtual void done() = 0; - -protected: -	item_ref_t mComplete; -	item_ref_t mIncomplete; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryFetchDescendentsObserver -// -// This class is much like the LLInventoryCompletionObserver, except -// that it handles fetching based on category. Override the done() -// method to do the thing you want. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryFetchDescendentsObserver : public LLInventoryObserver -{ -public: -	LLInventoryFetchDescendentsObserver() {} -	virtual void changed(U32 mask); - -	typedef std::vector<LLUUID> folder_ref_t; -	void fetchDescendents(const folder_ref_t& ids); -	bool isEverythingComplete() const; -	virtual void done() = 0; - -protected: -	bool isComplete(LLViewerInventoryCategory* cat); -	folder_ref_t mIncompleteFolders; -	folder_ref_t mCompleteFolders; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryFetchComboObserver -// -// This class does an appropriate combination of fetch descendents and -// item fetches based on completion of categories and items. Much like -// the fetch and fetch descendents, this will call done() when everything -// has arrived. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryFetchComboObserver : public LLInventoryObserver -{ -public: -	LLInventoryFetchComboObserver() : mDone(false) {} -	virtual void changed(U32 mask); - -	typedef std::vector<LLUUID> folder_ref_t; -	typedef std::vector<LLUUID> item_ref_t; -	void fetch(const folder_ref_t& folder_ids, const item_ref_t& item_ids); - -	virtual void done() = 0; - -protected: -	bool mDone; -	folder_ref_t mCompleteFolders; -	folder_ref_t mIncompleteFolders; -	item_ref_t mCompleteItems; -	item_ref_t mIncompleteItems; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryExistenceObserver -// -// This class is used as a base class for doing somethign when all the -// observed item ids exist in the inventory somewhere. You can derive -// a class from this class and implement the done() method to do -// something useful. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryExistenceObserver : public LLInventoryObserver -{ -public: -	LLInventoryExistenceObserver() {} -	virtual void changed(U32 mask); - -	void watchItem(const LLUUID& id); - -protected: -	virtual void done() = 0; - -	typedef std::vector<LLUUID> item_ref_t; -	item_ref_t mExist; -	item_ref_t mMIA; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryAddedObserver -// -// This class is used as a base class for doing something when  -// a new item arrives in inventory. -// It does not watch for a certain UUID, rather it acts when anything is added -// Derive a class from this class and implement the done() method to do -// something useful. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryAddedObserver : public LLInventoryObserver -{ -public: -	LLInventoryAddedObserver() : mAdded() {} -	virtual void changed(U32 mask); - -protected: -	virtual void done() = 0; - -	typedef std::vector<LLUUID> item_ref_t; -	item_ref_t mAdded; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryTransactionObserver -// -// Class which can be used as a base class for doing something when an -// inventory transaction completes. -// -// *NOTE: This class is not quite complete. Avoid using unless you fix up it's -// functionality gaps. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryTransactionObserver : public LLInventoryObserver -{ -public: -	LLInventoryTransactionObserver(const LLTransactionID& transaction_id); -	virtual void changed(U32 mask); - -protected: -	typedef std::vector<LLUUID> folder_ref_t; -	typedef std::vector<LLUUID> item_ref_t; -	virtual void done(const folder_ref_t& folders, const item_ref_t& items) = 0; - -	LLTransactionID mTransactionID; -}; - -  #endif // LL_LLINVENTORYMODEL_H diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp new file mode 100644 index 0000000000..3ccf593d27 --- /dev/null +++ b/indra/newview/llinventoryobserver.cpp @@ -0,0 +1,564 @@ +/**  + * @file llinventoryobserver.cpp + * @brief Implementation of the inventory observers used to track agent inventory. + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + *  + * Copyright (c) 2002-2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventoryobserver.h" + +#include "llassetstorage.h" +#include "llcrc.h" +#include "lldir.h" +#include "llsys.h" +#include "llxfermanager.h" +#include "message.h" + +#include "llagent.h" +#include "llagentwearables.h" +#include "llfloater.h" +#include "llfocusmgr.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "llviewermessage.h" +#include "llviewerwindow.h" +#include "llviewerregion.h" +#include "llappviewer.h" +#include "lldbstrings.h" +#include "llviewerstats.h" +#include "llmutelist.h" +#include "llnotifications.h" +#include "llcallbacklist.h" +#include "llpreview.h" +#include "llviewercontrol.h" +#include "llvoavatarself.h" +#include "llsdutil.h" +#include <deque> + +void LLInventoryCompletionObserver::changed(U32 mask) +{ +	// scan through the incomplete items and move or erase them as +	// appropriate. +	if(!mIncomplete.empty()) +	{ +		for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) +		{ +			LLViewerInventoryItem* item = gInventory.getItem(*it); +			if(!item) +			{ +				it = mIncomplete.erase(it); +				continue; +			} +			if(item->isComplete()) +			{ +				mComplete.push_back(*it); +				it = mIncomplete.erase(it); +				continue; +			} +			++it; +		} +		if(mIncomplete.empty()) +		{ +			done(); +		} +	} +} + +void LLInventoryCompletionObserver::watchItem(const LLUUID& id) +{ +	if(id.notNull()) +	{ +		mIncomplete.push_back(id); +	} +} + + +void LLInventoryFetchObserver::changed(U32 mask) +{ +	// scan through the incomplete items and move or erase them as +	// appropriate. +	if(!mIncomplete.empty()) +	{ +		for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) +		{ +			LLViewerInventoryItem* item = gInventory.getItem(*it); +			if(!item) +			{ +				// BUG: This can cause done() to get called prematurely below. +				// This happens with the LLGestureInventoryFetchObserver that +				// loads gestures at startup. JC +				it = mIncomplete.erase(it); +				continue; +			} +			if(item->isComplete()) +			{ +				mComplete.push_back(*it); +				it = mIncomplete.erase(it); +				continue; +			} +			++it; +		} +		if(mIncomplete.empty()) +		{ +			done(); +		} +	} +	//llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl; +	//llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl; +} + +bool LLInventoryFetchObserver::isEverythingComplete() const +{ +	return mIncomplete.empty(); +} + +void fetch_items_from_llsd(const LLSD& items_llsd) +{ +	if (!items_llsd.size()) return; +	LLSD body; +	body[0]["cap_name"] = "FetchInventory"; +	body[1]["cap_name"] = "FetchLib"; +	for (S32 i=0; i<items_llsd.size();i++) +	{ +		if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString()) +		{ +			body[0]["items"].append(items_llsd[i]); +			continue; +		} +		if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString()) +		{ +			body[1]["items"].append(items_llsd[i]); +			continue; +		} +	} +		 +	for (S32 i=0; i<body.size(); i++) +	{ +		if (0 >= body[i].size()) continue; +		std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); + +		if (!url.empty()) +		{ +			body[i]["agent_id"]	= gAgent.getID(); +			LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); +			break; +		} + +		LLMessageSystem* msg = gMessageSystem; +		BOOL start_new_message = TRUE; +		for (S32 j=0; j<body[i]["items"].size(); j++) +		{ +			LLSD item_entry = body[i]["items"][j]; +			if(start_new_message) +			{ +				start_new_message = FALSE; +				msg->newMessageFast(_PREHASH_FetchInventory); +				msg->nextBlockFast(_PREHASH_AgentData); +				msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); +				msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); +			} +			msg->nextBlockFast(_PREHASH_InventoryData); +			msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID()); +			msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID()); +			if(msg->isSendFull(NULL)) +			{ +				start_new_message = TRUE; +				gAgent.sendReliableMessage(); +			} +		} +		if(!start_new_message) +		{ +			gAgent.sendReliableMessage(); +		} +	} +} + +void LLInventoryFetchObserver::fetchItems( +	const LLInventoryFetchObserver::item_ref_t& ids) +{ +	LLUUID owner_id; +	LLSD items_llsd; +	for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it) +	{ +		LLViewerInventoryItem* item = gInventory.getItem(*it); +		if(item) +		{ +			if(item->isComplete()) +			{ +				// It's complete, so put it on the complete container. +				mComplete.push_back(*it); +				continue; +			} +			else +			{ +				owner_id = item->getPermissions().getOwner(); +			} +		} +		else +		{ +			// assume it's agent inventory. +			owner_id = gAgent.getID(); +		} +		 +		// It's incomplete, so put it on the incomplete container, and +		// pack this on the message. +		mIncomplete.push_back(*it); +		 +		// Prepare the data to fetch +		LLSD item_entry; +		item_entry["owner_id"] = owner_id; +		item_entry["item_id"] = (*it); +		items_llsd.append(item_entry); +	} +	fetch_items_from_llsd(items_llsd); +} + +// virtual +void LLInventoryFetchDescendentsObserver::changed(U32 mask) +{ +	for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) +	{ +		LLViewerInventoryCategory* cat = gInventory.getCategory(*it); +		if(!cat) +		{ +			it = mIncompleteFolders.erase(it); +			continue; +		} +		if(isComplete(cat)) +		{ +			mCompleteFolders.push_back(*it); +			it = mIncompleteFolders.erase(it); +			continue; +		} +		++it; +	} +	if(mIncompleteFolders.empty()) +	{ +		done(); +	} +} + +void LLInventoryFetchDescendentsObserver::fetchDescendents( +	const folder_ref_t& ids) +{ +	for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it) +	{ +		LLViewerInventoryCategory* cat = gInventory.getCategory(*it); +		if(!cat) continue; +		if(!isComplete(cat)) +		{ +			cat->fetchDescendents();		//blindly fetch it without seeing if anything else is fetching it. +			mIncompleteFolders.push_back(*it);	//Add to list of things being downloaded for this observer. +		} +		else +		{ +			mCompleteFolders.push_back(*it); +		} +	} +} + +bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const +{ +	return mIncompleteFolders.empty(); +} + +bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat) +{ +	S32 version = cat->getVersion(); +	S32 descendents = cat->getDescendentCount(); +	if((LLViewerInventoryCategory::VERSION_UNKNOWN == version) +	   || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents)) +	{ +		return false; +	} +	// it might be complete - check known descendents against +	// currently available. +	LLInventoryModel::cat_array_t* cats; +	LLInventoryModel::item_array_t* items; +	gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); +	if(!cats || !items) +	{ +		// bit of a hack - pretend we're done if they are gone or +		// incomplete. should never know, but it would suck if this +		// kept tight looping because of a corrupt memory state. +		return true; +	} +	S32 known = cats->count() + items->count(); +	if(descendents == known) +	{ +		// hey - we're done. +		return true; +	} +	return false; +} + +void LLInventoryFetchComboObserver::changed(U32 mask) +{ +	if(!mIncompleteItems.empty()) +	{ +		for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); ) +		{ +			LLViewerInventoryItem* item = gInventory.getItem(*it); +			if(!item) +			{ +				it = mIncompleteItems.erase(it); +				continue; +			} +			if(item->isComplete()) +			{ +				mCompleteItems.push_back(*it); +				it = mIncompleteItems.erase(it); +				continue; +			} +			++it; +		} +	} +	if(!mIncompleteFolders.empty()) +	{ +		for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) +		{ +			LLViewerInventoryCategory* cat = gInventory.getCategory(*it); +			if(!cat) +			{ +				it = mIncompleteFolders.erase(it); +				continue; +			} +			if(gInventory.isCategoryComplete(*it)) +			{ +				mCompleteFolders.push_back(*it); +				it = mIncompleteFolders.erase(it); +				continue; +			} +			++it; +		} +	} +	if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty()) +	{ +		mDone = true; +		done(); +	} +} + +void LLInventoryFetchComboObserver::fetch( +	const folder_ref_t& folder_ids, +	const item_ref_t& item_ids) +{ +	lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl; +	for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit) +	{ +		LLViewerInventoryCategory* cat = gInventory.getCategory(*fit); +		if(!cat) continue; +		if(!gInventory.isCategoryComplete(*fit)) +		{ +			cat->fetchDescendents(); +			lldebugs << "fetching folder " << *fit <<llendl; +			mIncompleteFolders.push_back(*fit); +		} +		else +		{ +			mCompleteFolders.push_back(*fit); +			lldebugs << "completing folder " << *fit <<llendl; +		} +	} + +	// Now for the items - we fetch everything which is not a direct +	// descendent of an incomplete folder because the item will show +	// up in an inventory descendents message soon enough so we do not +	// have to fetch it individually. +	LLSD items_llsd; +	LLUUID owner_id; +	for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit) +	{ +		LLViewerInventoryItem* item = gInventory.getItem(*iit); +		if(!item) +		{ +			lldebugs << "uanble to find item " << *iit << llendl; +			continue; +		} +		if(item->isComplete()) +		{ +			// It's complete, so put it on the complete container. +			mCompleteItems.push_back(*iit); +			lldebugs << "completing item " << *iit << llendl; +			continue; +		} +		else +		{ +			mIncompleteItems.push_back(*iit); +			owner_id = item->getPermissions().getOwner(); +		} +		if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end()) +		{ +			LLSD item_entry; +			item_entry["owner_id"] = owner_id; +			item_entry["item_id"] = (*iit); +			items_llsd.append(item_entry); +		} +		else +		{ +			lldebugs << "not worrying about " << *iit << llendl; +		} +	} +	fetch_items_from_llsd(items_llsd); +} + +void LLInventoryExistenceObserver::watchItem(const LLUUID& id) +{ +	if(id.notNull()) +	{ +		mMIA.push_back(id); +	} +} + +void LLInventoryExistenceObserver::changed(U32 mask) +{ +	// scan through the incomplete items and move or erase them as +	// appropriate. +	if(!mMIA.empty()) +	{ +		for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); ) +		{ +			LLViewerInventoryItem* item = gInventory.getItem(*it); +			if(!item) +			{ +				++it; +				continue; +			} +			mExist.push_back(*it); +			it = mMIA.erase(it); +		} +		if(mMIA.empty()) +		{ +			done(); +		} +	} +} + +void LLInventoryAddedObserver::changed(U32 mask) +{ +	if(!(mask & LLInventoryObserver::ADD)) +	{ +		return; +	} + +	// *HACK: If this was in response to a packet off +	// the network, figure out which item was updated. +	LLMessageSystem* msg = gMessageSystem; + +	std::string msg_name; +	if (mMessageName.empty()) +	{ +		msg_name = msg->getMessageName(); +	} +	else +	{ +		msg_name = mMessageName; +	} + +	if (msg_name.empty()) +	{ +		return; +	} +	 +	// We only want newly created inventory items. JC +	if ( msg_name != "UpdateCreateInventoryItem") +	{ +		return; +	} + +	LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; +	S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); +	for(S32 i = 0; i < num_blocks; ++i) +	{ +		titem->unpackMessage(msg, _PREHASH_InventoryData, i); +		if (!(titem->getUUID().isNull())) +		{ +			//we don't do anything with null keys +			mAdded.push_back(titem->getUUID()); +		} +	} +	if (!mAdded.empty()) +	{ +		done(); +	} +} + +LLInventoryTransactionObserver::LLInventoryTransactionObserver( +	const LLTransactionID& transaction_id) : +	mTransactionID(transaction_id) +{ +} + +void LLInventoryTransactionObserver::changed(U32 mask) +{ +	if(mask & LLInventoryObserver::ADD) +	{ +		// This could be it - see if we are processing a bulk update +		LLMessageSystem* msg = gMessageSystem; +		if(msg->getMessageName() +		   && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory"))) +		{ +			// we have a match for the message - now check the +			// transaction id. +			LLUUID id; +			msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id); +			if(id == mTransactionID) +			{ +				// woo hoo, we found it +				folder_ref_t folders; +				item_ref_t items; +				S32 count; +				count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); +				S32 i; +				for(i = 0; i < count; ++i) +				{ +					msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i); +					if(id.notNull()) +					{ +						folders.push_back(id); +					} +				} +				count = msg->getNumberOfBlocksFast(_PREHASH_ItemData); +				for(i = 0; i < count; ++i) +				{ +					msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i); +					if(id.notNull()) +					{ +						items.push_back(id); +					} +				} + +				// call the derived class the implements this method. +				done(folders, items); +			} +		} +	} +} diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h new file mode 100644 index 0000000000..384e6292e8 --- /dev/null +++ b/indra/newview/llinventoryobserver.h @@ -0,0 +1,249 @@ +/**  + * @file llinventoryobserver.h + * @brief LLInventoryObserver class header file + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + *  + * Copyright (c) 2002-2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLINVENTORYOBSERVERS_H +#define LL_LLINVENTORYOBSERVERS_H + +#include "lluuid.h" +#include <string> +#include <vector> + +class LLViewerInventoryCategory; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryObserver +// +// This class is designed to be a simple abstract base class which can +// relay messages when the inventory changes. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryObserver +{ +public: +	// This enumeration is a way to refer to what changed in a more +	// human readable format. You can mask the value provided by +	// chaged() to see if the observer is interested in the change. +	enum  +	{ +		NONE = 0, +		LABEL = 1,			// name changed +		INTERNAL = 2,		// internal change, eg, asset uuid different +		ADD = 4,			// something added +		REMOVE = 8,			// something deleted +		STRUCTURE = 16,		// structural change, eg, item or folder moved +		CALLING_CARD = 32,	// online, grant status, cancel, etc change +		ALL = 0xffffffff +	}; +	virtual ~LLInventoryObserver() {}; +	virtual void changed(U32 mask) = 0; +	std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328] +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryCompletionObserver +// +// Class which can be used as a base class for doing something when +// when all observed items are locally complete. This class implements +// the changed() method of LLInventoryObserver and declares a new +// method named done() which is called when all watched items have +// complete information in the inventory model. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryCompletionObserver : public LLInventoryObserver +{ +public: +	LLInventoryCompletionObserver() {} +	virtual void changed(U32 mask); + +	void watchItem(const LLUUID& id); + +protected: +	virtual void done() = 0; + +	typedef std::vector<LLUUID> item_ref_t; +	item_ref_t mComplete; +	item_ref_t mIncomplete; +}; + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryFetchObserver +// +// This class is much like the LLInventoryCompletionObserver, except +// that it handles all the the fetching necessary. Override the done() +// method to do the thing you want. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryFetchObserver : public LLInventoryObserver +{ +public: +	LLInventoryFetchObserver() {} +	virtual void changed(U32 mask); + +	typedef std::vector<LLUUID> item_ref_t; + +	bool isEverythingComplete() const; +	void fetchItems(const item_ref_t& ids); +	virtual void done() = 0; + +protected: +	item_ref_t mComplete; +	item_ref_t mIncomplete; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryFetchDescendentsObserver +// +// This class is much like the LLInventoryCompletionObserver, except +// that it handles fetching based on category. Override the done() +// method to do the thing you want. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryFetchDescendentsObserver : public LLInventoryObserver +{ +public: +	LLInventoryFetchDescendentsObserver() {} +	virtual void changed(U32 mask); + +	typedef std::vector<LLUUID> folder_ref_t; +	void fetchDescendents(const folder_ref_t& ids); +	bool isEverythingComplete() const; +	virtual void done() = 0; + +protected: +	bool isComplete(LLViewerInventoryCategory* cat); +	folder_ref_t mIncompleteFolders; +	folder_ref_t mCompleteFolders; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryFetchComboObserver +// +// This class does an appropriate combination of fetch descendents and +// item fetches based on completion of categories and items. Much like +// the fetch and fetch descendents, this will call done() when everything +// has arrived. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryFetchComboObserver : public LLInventoryObserver +{ +public: +	LLInventoryFetchComboObserver() : mDone(false) {} +	virtual void changed(U32 mask); + +	typedef std::vector<LLUUID> folder_ref_t; +	typedef std::vector<LLUUID> item_ref_t; +	void fetch(const folder_ref_t& folder_ids, const item_ref_t& item_ids); + +	virtual void done() = 0; + +protected: +	bool mDone; +	folder_ref_t mCompleteFolders; +	folder_ref_t mIncompleteFolders; +	item_ref_t mCompleteItems; +	item_ref_t mIncompleteItems; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryExistenceObserver +// +// This class is used as a base class for doing somethign when all the +// observed item ids exist in the inventory somewhere. You can derive +// a class from this class and implement the done() method to do +// something useful. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryExistenceObserver : public LLInventoryObserver +{ +public: +	LLInventoryExistenceObserver() {} +	virtual void changed(U32 mask); + +	void watchItem(const LLUUID& id); + +protected: +	virtual void done() = 0; + +	typedef std::vector<LLUUID> item_ref_t; +	item_ref_t mExist; +	item_ref_t mMIA; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryAddedObserver +// +// This class is used as a base class for doing something when  +// a new item arrives in inventory. +// It does not watch for a certain UUID, rather it acts when anything is added +// Derive a class from this class and implement the done() method to do +// something useful. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryAddedObserver : public LLInventoryObserver +{ +public: +	LLInventoryAddedObserver() : mAdded() {} +	virtual void changed(U32 mask); + +protected: +	virtual void done() = 0; + +	typedef std::vector<LLUUID> item_ref_t; +	item_ref_t mAdded; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryTransactionObserver +// +// Class which can be used as a base class for doing something when an +// inventory transaction completes. +// +// *NOTE: This class is not quite complete. Avoid using unless you fix up it's +// functionality gaps. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryTransactionObserver : public LLInventoryObserver +{ +public: +	LLInventoryTransactionObserver(const LLTransactionID& transaction_id); +	virtual void changed(U32 mask); + +protected: +	typedef std::vector<LLUUID> folder_ref_t; +	typedef std::vector<LLUUID> item_ref_t; +	virtual void done(const folder_ref_t& folders, const item_ref_t& items) = 0; + +	LLTransactionID mTransactionID; +}; + + +#endif // LL_LLINVENTORYOBSERVERS_H + diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 9a71e53441..dfd4af5c28 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -191,11 +191,9 @@ BOOL LLInventoryPanel::postBuild()  	{
  		rebuildViewsFor(mStartFolderID);
  		mHasInventoryConnection = true;
 +		defaultOpenInventory();
  	}
 -	// bit of a hack to make sure the inventory is open.
 -	mFolders->openFolder(preferred_type != LLFolderType::FT_NONE ? LLViewerFolderType::lookupNewCategoryName(preferred_type) : "My Inventory");
 -
  	if (mSortOrderSetting != INHERIT_SORT_ORDER)
  	{
  		setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
 @@ -300,6 +298,7 @@ void LLInventoryPanel::modelChanged(U32 mask)  	{
  		rebuildViewsFor(mStartFolderID);
  		mHasInventoryConnection = true;
 +		defaultOpenInventory();
  		return;
  	}
 @@ -561,6 +560,25 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id)  	}
  }
 +// bit of a hack to make sure the inventory is open.
 +void LLInventoryPanel::defaultOpenInventory()
 +{
 +	const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolderString);
 +	if (preferred_type != LLFolderType::FT_NONE)
 +	{
 +		const std::string& top_level_folder_name = LLViewerFolderType::lookupNewCategoryName(preferred_type);
 +		mFolders->openFolder(top_level_folder_name);
 +	}
 +	else
 +	{
 +		// Get the first child (it should be "My Inventory") and
 +		// open it up by name (just to make sure the first child is actually a folder).
 +		LLView* first_child = mFolders->getFirstChild();
 +		const std::string& first_child_name = first_child->getName();
 +		mFolders->openFolder(first_child_name);
 +	}
 +}
 +
  struct LLConfirmPurgeData
  {
  	LLUUID mID;
 diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 9f74fad5c1..e398c44105 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -165,7 +165,7 @@ protected:  	// Given the id and the parent, build all of the folder views.  	void rebuildViewsFor(const LLUUID& id);  	virtual void buildNewViews(const LLUUID& id); // made virtual to support derived classes. EXT-719 - +	void defaultOpenInventory(); // open the first level of inventory  protected:  	LLInventoryModel*			mInventory;  	LLInventoryObserver*		mInventoryObserver; diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 945294f3f2..a01426ea87 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -182,6 +182,9 @@ void LLLoginInstance::constructAuthParams(const LLSD& credentials)  	mRequestData["method"] = "login_to_simulator";  	mRequestData["params"] = request_params;  	mRequestData["options"] = requested_options; + +	mRequestData["cfg_srv_timeout"] = gSavedSettings.getF32("LoginSRVTimeout"); +	mRequestData["cfg_srv_pump"] = gSavedSettings.getString("LoginSRVPump");  }  bool LLLoginInstance::handleLoginEvent(const LLSD& event) diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 3993431311..94b8791147 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -36,7 +36,6 @@  #include "lltrans.h"  #include "llnearbychatbar.h" -#include "llspeakbutton.h"  #include "llbottomtray.h"  #include "llagent.h"  #include "llgesturemgr.h" @@ -234,14 +233,6 @@ BOOL LLNearbyChatBar::postBuild()  	mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator");  	mOutputMonitor->setVisible(FALSE); -	mSpeakBtn = getParent()->getChild<LLSpeakButton>("talk"); - -	// Speak button should be initially disabled because -	// it takes some time between logging in to world and connecting to voice channel. -	mSpeakBtn->setEnabled(FALSE); - -	// Registering Chat Bar to receive Voice client status change notifications. -	gVoiceClient->addObserver(this);  	return TRUE;  } @@ -733,27 +724,6 @@ public:  	}  }; -void LLNearbyChatBar::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ -	// Time it takes to connect to voice channel might be pretty long, -	// so don't expect user login or STATUS_VOICE_ENABLED to be followed by STATUS_JOINED. -	BOOL enable = FALSE; - -	switch (status) -	{ -	// Do not add STATUS_VOICE_ENABLED because voice chat is  -	// inactive until STATUS_JOINED -	case STATUS_JOINED: -		enable = TRUE; -		break; -	default: -		enable = FALSE; -		break; -	} - -	mSpeakBtn->setEnabled(enable); -} -  // Creating the object registers with the dispatcher.  LLChatHandler gChatHandler; diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index 56ee706a97..224118e088 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -42,9 +42,6 @@  #include "llspeakers.h" -class LLSpeakButton; - -  class LLGestureComboBox  	: public LLComboBox  	, public LLGestureManagerObserver @@ -76,7 +73,6 @@ protected:  class LLNearbyChatBar  :	public LLPanel -,   public LLVoiceClientStatusObserver  {  public:  	// constructor for inline chat-bars (e.g. hosted in chat history window) @@ -105,11 +101,6 @@ public:  	S32 getMinWidth() const;  	S32 getMaxWidth() const; -	/** -	 * Implements LLVoiceClientStatusObserver::onChange() -	 */ -	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); -  protected:  	static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str);  	static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); @@ -127,7 +118,6 @@ protected:  	static S32 sLastSpecialChatChannel;  	LLLineEditor*		mChatBox; -	LLSpeakButton*		mSpeakBtn;  	LLOutputMonitorCtrl* mOutputMonitor;  	LLLocalSpeakerMgr*  mSpeakerMgr;  }; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 15a75cb930..e3b2ab77aa 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -204,11 +204,6 @@ void LLPanelMainInventory::initListCommandsHandlers()  	mListCommands->childSetAction("options_gear_btn", boost::bind(&LLPanelMainInventory::onGearButtonClick, this));
  	mListCommands->childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this));
  	mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this));
 -	/*
 -	mListCommands->getChild<LLButton>("add_btn")->setHeldDownCallback(boost::bind(&LLPanelMainInventory::onAddButtonHeldDown, this));
 -	static const LLSD add_landmark_command("add_landmark");
 -	mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddAction, this, add_landmark_command));
 -	*/
  	LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>("trash_btn");
  	trash_btn->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this
 diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index 57ea018f25..51d53b2674 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -133,6 +133,16 @@ LLSpeakButton::~LLSpeakButton()  	LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn);  } +void LLSpeakButton::setSpeakToolTip(const std::string& msg) +{ +	mSpeakBtn->setToolTip(msg); +} + +void LLSpeakButton::setShowToolTip(const std::string& msg) +{ +	mShowBtn->setToolTip(msg); +} +  void LLSpeakButton::onMouseDown_SpeakBtn()  {  	bool down = true; diff --git a/indra/newview/llspeakbutton.h b/indra/newview/llspeakbutton.h index e213c562dd..02c8ab3890 100644 --- a/indra/newview/llspeakbutton.h +++ b/indra/newview/llspeakbutton.h @@ -62,6 +62,11 @@ public:  	/*virtual*/ ~LLSpeakButton();  	/*virtual*/ void draw(); +	// *HACK: Need to put tooltips in a translatable location, +	// the panel that contains this button. +	void setSpeakToolTip(const std::string& msg); +	void setShowToolTip(const std::string& msg); +  protected:  	friend class LLUICtrlFactory;  	LLSpeakButton(const Params& p); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index a3daca6fa4..9e064d8135 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -364,9 +364,9 @@ bool LLURLDispatcher::dispatchRightClick(const std::string& url)  bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url)  {  	// *NOTE: Text editors are considered sources of trusted URLs -	// in order to make objectim and avatar profile links in chat -	// history work.  While a malicious resident could chat an app -	// SLURL, the receiving resident will see it and must affirmatively +	// in order to make avatar profile links in chat history work. +	// While a malicious resident could chat an app SLURL, the +	// receiving resident will see it and must affirmatively  	// click on it.  	// *TODO: Make this trust model more refined.  JC  	const bool trusted_browser = true; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 7cd660110f..7772f613f0 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -112,6 +112,7 @@  #include "llinspectavatar.h"  #include "llinspectgroup.h"  #include "llinspectobject.h" +#include "llinspectremoteobject.h"  #include "llmediaremotectrl.h"  #include "llmoveview.h"  #include "llnearbychat.h" @@ -176,6 +177,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLInspectAvatarUtil::registerFloater();  	LLInspectGroupUtil::registerFloater();  	LLInspectObjectUtil::registerFloater(); +	LLInspectRemoteObjectUtil::registerFloater();  	LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLagMeter>);  	LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 976d89a5b7..728fb7c616 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1879,7 +1879,9 @@ class LLAdvancedDebugAvatarTextures : public view_listener_t  {  	bool handleEvent(const LLSD& userdata)  	{ +#ifndef LL_RELEASE_FOR_DOWNLOAD  		handle_debug_avatar_textures(NULL); +#endif  		return true;  	}  }; @@ -1893,7 +1895,9 @@ class LLAdvancedDumpAvatarLocalTextures : public view_listener_t  {  	bool handleEvent(const LLSD& userdata)  	{ +#ifndef LL_RELEASE_FOR_DOWNLOAD  		handle_dump_avatar_local_textures(NULL); +#endif  		return true;  	}  }; @@ -7860,10 +7864,8 @@ void initialize_menus()  	view_listener_t::addMenu(new LLAdvancedCheckDebugCharacterVis(), "Advanced.CheckDebugCharacterVis");  	view_listener_t::addMenu(new LLAdvancedDumpAttachments(), "Advanced.DumpAttachments");  	view_listener_t::addMenu(new LLAdvancedRebakeTextures(), "Advanced.RebakeTextures"); -	#ifndef LL_RELEASE_FOR_DOWNLOAD  	view_listener_t::addMenu(new LLAdvancedDebugAvatarTextures(), "Advanced.DebugAvatarTextures");  	view_listener_t::addMenu(new LLAdvancedDumpAvatarLocalTextures(), "Advanced.DumpAvatarLocalTextures"); -	#endif  	// Advanced > Network  	view_listener_t::addMenu(new LLAdvancedEnableMessageLog(), "Advanced.EnableMessageLog");  	view_listener_t::addMenu(new LLAdvancedDisableMessageLog(), "Advanced.DisableMessageLog"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1a4c849f25..4088eafe16 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1431,6 +1431,17 @@ bool goto_url_callback(const LLSD& notification, const LLSD& response)  }  static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); +bool inspect_remote_object_callback(const LLSD& notification, const LLSD& response) +{ +	S32 option = LLNotification::getSelectedOption(notification, response); +	if (0 == option) +	{ +		LLFloaterReg::showInstance("inspect_remote_object", notification["payload"]); +	} +	return false; +} +static LLNotificationFunctorRegistration inspect_remote_object_callback_reg("ServerObjectMessage", inspect_remote_object_callback); +  void process_improved_im(LLMessageSystem *msg, void **user_data)  {  	if (gNoRender) @@ -1943,9 +1954,23 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)  				return;  			} +			// Build a link to open the object IM info window. +			std::string location = ll_safe_string((char*)binary_bucket, binary_bucket_size-1); +  			LLSD substitutions; +			substitutions["NAME"] = name;  			substitutions["MSG"] = message; -			LLNotifications::instance().add("ServerObjectMessage", substitutions); + +			LLSD payload; +			payload["object_id"] = session_id; +			payload["owner_id"] = from_id; +			payload["slurl"] = location; +			payload["name"] = name; +			if (from_group) +			{ +				payload["groupowned"] = "true"; +			} +			LLNotifications::instance().add("ServerObjectMessage", substitutions, payload);  		}  		break;  	case IM_FROM_TASK_AS_ALERT: diff --git a/indra/newview/skins/default/xui/en/floater_inventory.xml b/indra/newview/skins/default/xui/en/floater_inventory.xml index 30639f955f..b48c962413 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory.xml @@ -36,10 +36,10 @@  	 filename="panel_main_inventory.xml"  	 follows="all"  	 layout="topleft" +	 hide_top_panel="true"  	 left="0"  	 label="Inventory Panel"  	 name="Inventory Panel"  	 top="15" -	 width="467"> -</panel> +	 width="467" />  </floater> diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml index b9cf456842..d9498586af 100644 --- a/indra/newview/skins/default/xui/en/floater_search.xml +++ b/indra/newview/skins/default/xui/en/floater_search.xml @@ -25,7 +25,7 @@          Done      </floater.string>      <layout_stack -     bottom="400" +     bottom="512"       follows="left|right|top|bottom"       layout="topleft"       left="10" @@ -54,7 +54,7 @@               layout="topleft"               left_delta="0"               name="status_text" -             top_pad="4" +             top_pad="5"               width="150" />          </layout_panel>      </layout_stack> diff --git a/indra/newview/skins/default/xui/en/inspect_remote_object.xml b/indra/newview/skins/default/xui/en/inspect_remote_object.xml new file mode 100644 index 0000000000..07c684d904 --- /dev/null +++ b/indra/newview/skins/default/xui/en/inspect_remote_object.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- +  Not can_close / no title to avoid window chrome +  Single instance - only have one at a time, recycle it each spawn +--> +<floater + legacy_header_height="18" + bevel_style="in" + bg_opaque_image="Inspector_Background"  + can_close="false" + can_minimize="false" + height="145" + layout="topleft" + name="inspect_remote_object" + single_instance="true" + sound_flags="0" + visible="true" + width="300"> +  <text +   follows="all" +   font="SansSerifLargeBold" +   height="16" +   left="8" +   name="object_name" +   text_color="White" +   top="5" +   use_ellipses="true" +   width="290"> +     Test Object Name That Is Really Long +  </text> +  <text +   follows="all" +   font="SansSerif" +   height="20" +   left="8" +   name="object_owner_label" +   width="55" +   top_pad="20"> +     Owner: +  </text> +  <text +   follows="top|left" +   font="SansSerif" +   height="20" +   left_pad="10" +   name="object_owner" +   use_ellipses="true" +   width="200" +   word_wrap="false"> +     Longavatarname Johnsonlongstonnammer +  </text> +  <text +   follows="top|left" +   font="SansSerif" +   height="20" +   left="8" +   name="object_slurl_label" +   top_pad="10" +   width="55"> +     Location: +  </text> +  <text +   follows="top|left" +   height="20" +   left_pad="10" +   name="object_slurl" +   width="240" +   use_ellipses="true" +   word_wrap="false"> +     http://slurl.com/Ahern/50/50/50 +  </text> +  <button +   follows="top|left" +   font="SansSerif" +   height="20" +   label="Map" +   left="10" +   name="map_btn" +   top="114" +   width="75" /> +  <button +   follows="top|left" +   font="SansSerif" +   height="20" +   label="Block" +   left_pad="5" +   name="block_btn" +   top_delta="0" +   width="75" /> +  <button +   follows="top|left" +   font="SansSerif" +   height="20" +   label="Close" +   right="-10" +   name="close_btn" +   top_delta="0" +   width="75" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 96c6d970c2..ff0cd7ffeb 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4696,7 +4696,12 @@ The objects on the selected parcel that are NOT owned by you have been returned     icon="notify.tga"     name="ServerObjectMessage"     type="notify"> +Message from [NAME]:  [MSG] +    <usetemplate +     name="okcancelbuttons" +     notext="OK" +     yestext="Inspect"/>    </notification>    <notification diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 8188016455..3c16a439d9 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -13,6 +13,8 @@   chrome="true"   border_visible="false"   width="1000"> +  <string name="SpeakBtnToolTip">Turns microphone on/off</string> +  <string name="VoiceControlBtnToolTip">Shows/hides voice control panel</string>      <layout_stack       mouse_opaque="false"       border_size="0" @@ -70,9 +72,7 @@             left="0"             name="talk"             top="3" -          width="100" -          speak_button.tool_tip="Turns microphone on/off" -          show_button.tool_tip="Shows/hides voice control panel" /> +          width="100" />          </layout_panel>          <icon              auto_resize="false" diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index d31a81e128..7b28a3b72c 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -76,6 +76,7 @@ LLControlGroup::LLControlGroup(const std::string& name) :  LLControlGroup::~LLControlGroup() {}  void LLControlGroup::setBOOL(const std::string& name, BOOL val) {}  BOOL LLControlGroup::getBOOL(const std::string& name) { return FALSE; } +F32 LLControlGroup::getF32(const std::string& name) { return 0.0f; }  U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only) { return 1; }  void LLControlGroup::setString(const std::string& name, const std::string& val) {}  std::string LLControlGroup::getString(const std::string& name) { return "test_string"; } diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 7a30315b9a..b14c59ab9a 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -70,7 +70,7 @@ public:  	LLEventPump& getEventPump() { return mPump; }  private: -	void sendProgressEvent(const std::string& state, const std::string& change, +	LLSD getProgressEventLLSD(const std::string& state, const std::string& change,  						   const LLSD& data = LLSD())  	{  		LLSD status_data; @@ -87,7 +87,13 @@ private:  		{  			status_data["data"] = data;  		} +		return status_data; +	} +	void sendProgressEvent(const std::string& state, const std::string& change, +						   const LLSD& data = LLSD()) +	{ +		LLSD status_data = getProgressEventLLSD(state, change, data);  		mPump.post(status_data);  	} @@ -140,15 +146,28 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credential          // Request SRV record.          LL_INFOS("LLLogin") << "Requesting SRV record from " << uri << LL_ENDL; -        // *NOTE:Mani - Completely arbitrary timeout value for SRV request. -        filter.errorAfter(5, "SRV Request timed out!"); +        // *NOTE:Mani - Completely arbitrary default timeout value for SRV request. +		F32 seconds_to_timeout = 5.0f; +		if(credentials.has("cfg_srv_timeout")) +		{ +			seconds_to_timeout = credentials["cfg_srv_timeout"].asReal(); +		} + +		filter.eventAfter(seconds_to_timeout,  +			getProgressEventLLSD("offline", "fail.login")); + +		std::string srv_pump_name = "LLAres"; +		if(credentials.has("cfg_srv_pump")) +		{ +			srv_pump_name = credentials["cfg_srv_pump"].asString(); +		} -        // Make request +		// Make request          LLSD request;          request["op"] = "rewriteURI";          request["uri"] = uri;          request["reply"] = replyPump.getName(); -        rewrittenURIs = postAndWait(self, request, "LLAres", filter); +        rewrittenURIs = postAndWait(self, request, srv_pump_name, filter);      } // we no longer need the filter      LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index a8ae2883d5..56c21016bd 100644 --- a/indra/viewer_components/login/tests/lllogin_test.cpp +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -47,6 +47,7 @@ public:  	bool call(const LLSD& event)  	{  		mDebug(STRINGIZE("LoginListener called!: " << event)); +		  		mLastEvent = event;  		return false;  	} @@ -414,4 +415,40 @@ namespace tut  		ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline");  	} + +    template<> template<> +    void llviewerlogin_object::test<5>() +    { +        DEBUG; +		// Test SRV request timeout. +		set_test_name("LLLogin SRV timeout testing"); + +		// Testing normal login procedure. +		LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. + +		// LLAresListener dummyLLAres("dummy_llares"); +		// dummyLLAres.listenTo(llaresPump); + +		LLLogin login; +		LoginListener listener("test_ear"); +		listener.listenTo(login.getEventPump()); + +		LLSD credentials; +		credentials["first"] = "these"; +		credentials["last"] = "don't"; +		credentials["passwd"] = "matter"; +		credentials["cfg_srv_timeout"] = 0.0f; + +		login.connect("login.bar.com", credentials); + +		ensure_equals("SRV State", listener.lastEvent()["change"].asString(), "srvrequest");  + +		// Get the mainloop eventpump, which needs a pinging in order to drive the  +		// SRV timeout. +		LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); +		LLSD frame_event; +		mainloop.post(frame_event); + +		ensure_equals("SRV Failure", listener.lastEvent()["change"].asString(), "fail.login");  +	}  } | 
