diff options
| author | Yuri Chebotarev <ychebotarev@productengine.com> | 2010-04-23 15:51:07 +0300 | 
|---|---|---|
| committer | Yuri Chebotarev <ychebotarev@productengine.com> | 2010-04-23 15:51:07 +0300 | 
| commit | 6113dab230074c641d659b48f76c8799cd46b741 (patch) | |
| tree | 6cb1fff06c4fb67cee698605c6b66cebc9845523 | |
| parent | 92450263ec6eb31bd8fae233c3b6fef2f200db40 (diff) | |
| parent | efd7b2502df521e945274db74f88297a358e5144 (diff) | |
merge
--HG--
branch : product-engine
22 files changed, 715 insertions, 159 deletions
| diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index db0b44eb8f..bfaf3f4f26 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -75,11 +75,33 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")  if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")    set(DARWIN 1) -  # set this dynamically from the build system now - -  # NOTE: wont have a distributable build unless you add this on the configure line with: + +  # NOTE: If specifying a different SDK with CMAKE_OSX_SYSROOT at configure +  # time you should also specify CMAKE_OSX_DEPLOYMENT_TARGET explicitly, +  # otherwise CMAKE_OSX_SYSROOT will be overridden here. We can't just check +  # for it being unset, as it gets set to the system default :( + +  # Default to building against the 10.4 SDK if no deployment target is +  # specified. +  if (NOT CMAKE_OSX_DEPLOYMENT_TARGET) +    # NOTE: setting -isysroot is NOT adequate: http://lists.apple.com/archives/Xcode-users/2007/Oct/msg00696.html +    # see http://public.kitware.com/Bug/view.php?id=9959 + poppy +    set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.4u.sdk) +    set(CMAKE_OSX_DEPLOYMENT_TARGET 10.4) +  endif (NOT CMAKE_OSX_DEPLOYMENT_TARGET) + +  # GCC 4.2 is incompatible with the MacOSX 10.4 SDK +  if (${CMAKE_OSX_SYSROOT} MATCHES "10.4u") +    set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "4.0") +  endif (${CMAKE_OSX_SYSROOT} MATCHES "10.4u") + +  # NOTE: To attempt an i386/PPC Universal build, add this on the configure line:    # -DCMAKE_OSX_ARCHITECTURES:STRING='i386;ppc' -  #set(CMAKE_OSX_ARCHITECTURES i386;ppc) -  set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.4u.sdk) +  # Build only for i386 by default, system default on MacOSX 10.6 is x86_64 +  if (NOT CMAKE_OSX_ARCHITECTURES) +    set(CMAKE_OSX_ARCHITECTURES i386) +  endif (NOT CMAKE_OSX_ARCHITECTURES) +    if (CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")      set(ARCH universal)    else (CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc") @@ -89,6 +111,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")        set(ARCH i386)      endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "ppc")    endif (CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc") +    set(LL_ARCH ${ARCH}_darwin)    set(LL_ARCH_DIR universal-darwin)    set(WORD_SIZE 32) diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 65ef53443b..7b8f51ae3c 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -48,122 +48,38 @@  const std::string NOTIFICATION_PERSIST_VERSION = "0.93"; -// local channel for notification history -class LLNotificationHistoryChannel : public LLNotificationChannel +// Local channel for persistent notifications +// Stores only persistent notifications. +// Class users can use connectChanged() to process persistent notifications +// (see LLNotificationStorage for example). +class LLPersistentNotificationChannel : public LLNotificationChannel  { -	LOG_CLASS(LLNotificationHistoryChannel); +	LOG_CLASS(LLPersistentNotificationChannel);  public: -	LLNotificationHistoryChannel(const std::string& filename) :  -		LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()), -		mFileName(filename) +	LLPersistentNotificationChannel() : +		LLNotificationChannel("Persistent", "Visible", ¬ificationFilter, LLNotificationComparators::orderByUUID())  	{ -		connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1)); -		loadPersistentNotifications();  	}  private: -	bool historyHandler(const LLSD& payload) -	{ -		// we ignore "load" messages, but rewrite the persistence file on any other -		std::string sigtype = payload["sigtype"]; -		if (sigtype != "load") -		{ -			savePersistentNotifications(); -		} -		return false; -	} - -	// The history channel gets all notifications except those that have been cancelled -	static bool historyFilter(LLNotificationPtr pNotification) -	{ -		return !pNotification->isCancelled(); -	} -	void savePersistentNotifications() +	// The channel gets all persistent notifications except those that have been canceled +	static bool notificationFilter(LLNotificationPtr pNotification)  	{ -		/* NOTE: As of 2009-11-09 the reload of notifications on startup does not -		work, and has not worked for months.  Skip saving notifications until the -		read can be fixed, because this hits the disk once per notification and -		causes log spam.  James - -		llinfos << "Saving open notifications to " << mFileName << llendl; +		bool handle_notification = false; -		llofstream notify_file(mFileName.c_str()); -		if (!notify_file.is_open())  -		{ -			llwarns << "Failed to open " << mFileName << llendl; -			return; -		} - -		LLSD output; -		output["version"] = NOTIFICATION_PERSIST_VERSION; -		LLSD& data = output["data"]; - -		for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) -		{ -			if (!LLNotifications::instance().templateExists((*it)->getName())) continue; +		handle_notification = pNotification->isPersistent() +			&& !pNotification->isCancelled(); -			// only store notifications flagged as persisting -			LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName()); -			if (!templatep->mPersist) continue; - -			data.append((*it)->asLLSD()); -		} - -		LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter(); -		formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); -		*/ +		return handle_notification;  	} -	void loadPersistentNotifications() -	{ -		llinfos << "Loading open notifications from " << mFileName << llendl; - -		llifstream notify_file(mFileName.c_str()); -		if (!notify_file.is_open())  -		{ -			llwarns << "Failed to open " << mFileName << llendl; -			return; -		} - -		LLSD input; -		LLPointer<LLSDParser> parser = new LLSDXMLParser(); -		if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0) -		{ -			llwarns << "Failed to parse open notifications" << llendl; -			return; -		} - -		if (input.isUndefined()) return; -		std::string version = input["version"]; -		if (version != NOTIFICATION_PERSIST_VERSION) -		{ -			llwarns << "Bad open notifications version: " << version << llendl; -			return; -		} -		LLSD& data = input["data"]; -		if (data.isUndefined()) return; - -		LLNotifications& instance = LLNotifications::instance(); -		for (LLSD::array_const_iterator notification_it = data.beginArray(); -			notification_it != data.endArray(); -			++notification_it) -		{ -			instance.add(LLNotificationPtr(new LLNotification(*notification_it))); -		} -	} - -	//virtual  	void onDelete(LLNotificationPtr pNotification)  	{ -		// we want to keep deleted notifications in our log +		// we want to keep deleted notifications in our log, otherwise some  +		// notifications will be lost on exit.  		mItems.insert(pNotification); -		 -		return;  	} -	 -private: -	std::string mFileName;  };  bool filterIgnoredNotifications(LLNotificationPtr notification) @@ -417,6 +333,10 @@ LLNotification::LLNotification(const LLNotification::Params& p) :  		mTemporaryResponder = true;  	} +	else if(p.functor.responder.isChosen()) +	{ +		mResponder = p.functor.responder; +	}  	if(p.responder.isProvided())  	{ @@ -462,6 +382,12 @@ LLSD LLNotification::asLLSD()  	output["priority"] = (S32)mPriority;  	output["responseFunctor"] = mResponseFunctorName;  	output["reusable"] = mIsReusable; + +	if(mResponder) +	{ +		output["responder"] = mResponder->asLLSD(); +	} +  	return output;  } @@ -571,12 +497,20 @@ void LLNotification::respond(const LLSD& response)  	// *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()  	mRespondedTo = true;  	mResponse = response; -	// look up the functor -	LLNotificationFunctorRegistry::ResponseFunctor functor =  -		LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName); -	// and then call it -	functor(asLLSD(), response); -	 + +	if(mResponder) +	{ +		mResponder->handleRespond(asLLSD(), response); +	} +	else +	{ +		// look up the functor +		LLNotificationFunctorRegistry::ResponseFunctor functor = +			LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName); +		// and then call it +		functor(asLLSD(), response); +	} +  	if (mTemporaryResponder && !isReusable())  	{  		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); @@ -621,6 +555,11 @@ void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::Res  	LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);  } +void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder) +{ +	mResponder = responder; +} +  bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const  {  	for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();  @@ -1116,12 +1055,9 @@ void LLNotifications::createDefaultChannels()  	LLNotificationChannel::buildChannel("Visible", "Ignore",  		&LLNotificationFilters::includeEverything); -	// create special history channel -	//std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" ); -	// use ^^^ when done debugging notifications serialization -	std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" ); +	// create special persistent notification channel  	// this isn't a leak, don't worry about the empty "new" -	new LLNotificationHistoryChannel(notifications_log_file); +	new LLPersistentNotificationChannel();  	// connect action methods to these channels  	LLNotifications::instance().getChannel("Expiration")-> diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 1799ca65b7..c942a32512 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -116,8 +116,23 @@ typedef enum e_notification_priority  	NOTIFICATION_PRIORITY_CRITICAL  } ENotificationPriority; +class LLNotificationResponderInterface +{ +public: +	LLNotificationResponderInterface(){}; +	virtual ~LLNotificationResponderInterface(){}; + +	virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0; + +	virtual LLSD asLLSD() = 0; + +	virtual void fromLLSD(const LLSD& params) = 0; +}; +  typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder; +typedef boost::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr; +  typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;  typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration; @@ -303,10 +318,12 @@ public:  		{  			Alternative<std::string>										name;  			Alternative<LLNotificationFunctorRegistry::ResponseFunctor>	function; +			Alternative<LLNotificationResponderPtr>						responder;  			Functor()  			:	name("functor_name"), -				function("functor") +				function("functor"), +				responder("responder")  			{}  		};  		Optional<Functor>						functor; @@ -349,12 +366,13 @@ private:  	bool mIgnored;  	ENotificationPriority mPriority;  	LLNotificationFormPtr mForm; -	void* mResponderObj; +	void* mResponderObj; // TODO - refactor/remove this field  	bool mIsReusable; -	 +	LLNotificationResponderPtr mResponder; +  	// a reference to the template  	LLNotificationTemplatePtr mTemplatep; -	 +  	/*  	 We want to be able to store and reload notifications so that they can survive  	 a shutdown/restart of the client. So we can't simply pass in callbacks; @@ -393,6 +411,8 @@ public:  	void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb); +	void setResponseFunctor(const LLNotificationResponderPtr& responder); +  	typedef enum e_response_template_type  	{  		WITHOUT_DEFAULT_BUTTON, @@ -459,7 +479,12 @@ public:  	{  		return mTemplatep->mName;  	} -	 + +	bool isPersistent() const +	{ +		return mTemplatep->mPersist; +	} +  	const LLUUID& id() const  	{  		return mId; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index bfb5798909..99ba356d9e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -296,6 +296,7 @@ set(viewer_SOURCE_FILES      llnotificationmanager.cpp      llnotificationofferhandler.cpp      llnotificationscripthandler.cpp +    llnotificationstorage.cpp      llnotificationtiphandler.cpp      lloutfitslist.cpp      lloutputmonitorctrl.cpp @@ -802,6 +803,7 @@ set(viewer_HEADER_FILES      llnetmap.h      llnotificationhandler.h      llnotificationmanager.h +    llnotificationstorage.h      lloutfitslist.h      lloutputmonitorctrl.h      llpanelavatar.h @@ -1400,7 +1402,7 @@ if (WINDOWS)      # If adding a file to viewer_manifest.py in the WindowsManifest.construct() method, be sure to add the dependency      # here. -    # *NOTE:Mani - This is a crappy hack to have important dependecies for the viewer_manifest copy action +    # *NOTE:Mani - This is a crappy hack to have important dependencies for the viewer_manifest copy action      # be met. I'm looking forward to a source-code split-up project next year that will address this kind of thing.      # In the meantime, if you have any ideas on how to easily maintain one list, either here or in viewer_manifest.py      # and have the build deps get tracked *please* tell me about it. @@ -1421,7 +1423,7 @@ if (WINDOWS)      endif(USE_GOOGLE_PERFTOOLS) -    set(COPY_INPUT_DEPENDECIES +    set(COPY_INPUT_DEPENDENCIES        # The following commented dependencies are determined at variably at build time. Can't do this here.        #${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libtcmalloc_minimal.dll => None ... Skipping libtcmalloc_minimal.dll        ${CMAKE_SOURCE_DIR}/../etc/message.xml @@ -1517,7 +1519,7 @@ if (WINDOWS)        DEPENDS           ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py          stage_third_party_libs -        ${COPY_INPUT_DEPENDECIES} +        ${COPY_INPUT_DEPENDENCIES}        COMMENT "Performing viewer_manifest copy"        ) diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 466f2d499d..8a880e5ace 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -2031,6 +2031,39 @@ void LLAgentWearables::animateAllWearableParams(F32 delta, BOOL upload_bake)  	}  } +bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool closer_to_body) +{ +	if (!item) return false; +	if (!item->isWearableType()) return false; + +	wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(item->getWearableType()); +	if (wearable_iter == mWearableDatas.end()) return false; + +	wearableentry_vec_t& wearable_vec = wearable_iter->second; +	if (wearable_vec.empty()) return false; + +	const LLUUID& asset_id = item->getAssetUUID(); + +	//nowhere to move if the wearable is already on any boundary (closest to the body/furthest from the body) +	if (closer_to_body && asset_id == wearable_vec.front()->getAssetID()) return false; +	if (!closer_to_body && asset_id == wearable_vec.back()->getAssetID()) return false; + +	for (U32 i = 0; i < wearable_vec.size(); ++i) +	{ +		LLWearable* wearable = wearable_vec[i]; +		if (!wearable) continue; +		if (wearable->getAssetID() != asset_id) continue; +		 +		//swapping wearables +		U32 swap_i = closer_to_body ? i-1 : i+1; +		wearable_vec[i] = wearable_vec[swap_i]; +		wearable_vec[swap_i] = wearable; +		return true; +	} + +	return false; +} +  void LLAgentWearables::updateServer()  {  	sendAgentWearablesUpdate(); diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 585fd3f8b3..d3b18f68f1 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -82,6 +82,8 @@ public:  	void			animateAllWearableParams(F32 delta, BOOL upload_bake); +	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); +  	//--------------------------------------------------------------------  	// Accessors  	//-------------------------------------------------------------------- diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 9e02886e4f..a6a3aa28d6 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -122,6 +122,38 @@ private:  	bool mAppend;  }; + +//Inventory callback updating "dirty" state when destroyed +class LLUpdateDirtyState: public LLInventoryCallback +{ +public: +	LLUpdateDirtyState() {} +	virtual ~LLUpdateDirtyState(){ LLAppearanceMgr::getInstance()->updateIsDirty(); } +	virtual void fire(const LLUUID&) {} +}; + + +//Inventory collect functor collecting wearables of a specific wearable type +class LLFindClothesOfType : public LLInventoryCollectFunctor +{ +public: +	LLFindClothesOfType(EWearableType type) : mWearableType(type) {} +	virtual ~LLFindClothesOfType() {} +	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) +	{ +		if (!item) return false; +		if (item->getType() != LLAssetType::AT_CLOTHING) return false; +		 +		LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item); +		if (!vitem || vitem->getWearableType() != mWearableType) return false; + +		return true; +	} + +	const EWearableType mWearableType; +}; + +  LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy():  	mFireCount(0)  { @@ -1593,8 +1625,11 @@ bool LLAppearanceMgr::updateBaseOutfit()  	// in a Base Outfit we do not remove items, only links  	purgeCategory(base_outfit_id, false); + +	LLPointer<LLInventoryCallback> dirty_state_updater = new LLUpdateDirtyState(); +  	//COF contains only links so we copy to the Base Outfit only links -	shallowCopyCategoryContents(getCOF(), base_outfit_id, NULL); +	shallowCopyCategoryContents(getCOF(), base_outfit_id, dirty_state_updater);  	return true;  } @@ -1802,6 +1837,62 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)  	}  } + +bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) +{ +	if (!item || !item->isWearableType()) return false; +	if (item->getType() != LLAssetType::AT_CLOTHING) return false; +	if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false; + +	LLInventoryModel::cat_array_t cats; +	LLInventoryModel::item_array_t items; +	gInventory.collectDescendentsIf(getCOF(), cats, items, true, LLFindClothesOfType(item->getWearableType())); +	if (items.empty()) return false; + +	//*TODO all items are not guarantied to have valid descriptions (check?) +	std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType())); + +	if (closer_to_body && items.front() == item) return false; +	if (!closer_to_body && items.back() == item) return false; +	 +	LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item); +	if (items.end() == it) return false; + + +	//swapping descriptions +	closer_to_body ? --it : ++it; +	LLViewerInventoryItem* swap_item = *it; +	if (!swap_item) return false; +	std::string tmp = swap_item->LLInventoryItem::getDescription(); +	swap_item->setDescription(item->LLInventoryItem::getDescription()); +	item->setDescription(tmp); + + +	//items need to be updated on a dataserver +	item->setComplete(TRUE); +	item->updateServer(FALSE); +	gInventory.updateItem(item); + +	swap_item->setComplete(TRUE); +	swap_item->updateServer(FALSE); +	gInventory.updateItem(swap_item); + +	//to cause appearance of the agent to be updated +	bool result = false; +	if (result = gAgentWearables.moveWearable(item, closer_to_body)) +	{ +		gAgentAvatarp->wearableUpdated(item->getWearableType(), TRUE); +	} + +	setOutfitDirty(true); + +	//*TODO do we need to notify observers here in such a way? +	gInventory.notifyObservers(); + +	return result; +} + +  //#define DUMP_CAT_VERBOSE  void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg) diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index efb5274c5b..a308a3efa9 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -141,6 +141,8 @@ public:  	LLUUID makeNewOutfitLinks(const std::string& new_folder_name); +	bool moveWearable(LLViewerInventoryItem* item, bool closer_to_body); +  protected:  	LLAppearanceMgr();  	~LLAppearanceMgr(); diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 769387c26c..4f9434030f 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -35,6 +35,7 @@  #include "llchannelmanager.h"  #include "llappviewer.h" +#include "llnotificationstorage.h"  #include "llviewercontrol.h"  #include "llviewerwindow.h"  #include "llrootview.h" @@ -107,31 +108,35 @@ void LLChannelManager::onLoginCompleted()  	if(!away_notifications)  	{  		onStartUpToastClose(); -		return;  	} -	 -	// create a channel for the StartUp Toast -	LLChannelManager::Params p; -	p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID")); -	p.channel_align = CA_RIGHT; -	mStartUpChannel = createChannel(p); - -	if(!mStartUpChannel) +	else  	{ -		onStartUpToastClose(); -		return; -	} +		// create a channel for the StartUp Toast +		LLChannelManager::Params p; +		p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID")); +		p.channel_align = CA_RIGHT; +		mStartUpChannel = createChannel(p); -	gViewerWindow->getRootView()->addChild(mStartUpChannel); +		if(!mStartUpChannel) +		{ +			onStartUpToastClose(); +		} +		else +		{ +			gViewerWindow->getRootView()->addChild(mStartUpChannel); -	// init channel's position and size -	S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin");  -	S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); -	mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); -	mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4)); +			// init channel's position and size +			S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin");  +			S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); +			mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); +			mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4)); + +			mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); +			mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime")); +		} +	} -	mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); -	mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime")); +	LLPersistentNotificationStorage::getInstance()->loadNotifications();  }  //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationstorage.cpp b/indra/newview/llnotificationstorage.cpp new file mode 100644 index 0000000000..316ff4324c --- /dev/null +++ b/indra/newview/llnotificationstorage.cpp @@ -0,0 +1,228 @@ +/** +* @file llnotificationstorage.cpp +* @brief LLPersistentNotificationStorage class implementation +* +* $LicenseInfo:firstyear=2010&license=viewergpl$ +* +* Copyright (c) 2010, 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" // must be first include +#include "llnotificationstorage.h" + +#include "llxmlnode.h" // for linux compilers + +#include "llchannelmanager.h" +#include "llscreenchannel.h" +#include "llscriptfloater.h" +#include "llsdserialize.h" +#include "llviewermessage.h" + +////////////////////////////////////////////////////////////////////////// + +class LLResponderRegistry +{ +public: + +	static void registerResponders(); + +	static LLNotificationResponderInterface* createResponder(const std::string& notification_name, const LLSD& params); + +private: + +	template<typename RESPONDER_TYPE> +	static LLNotificationResponderInterface* create(const LLSD& params) +	{ +		RESPONDER_TYPE* responder = new RESPONDER_TYPE(); +		responder->fromLLSD(params); +		return responder; +	} + +	typedef boost::function<LLNotificationResponderInterface* (const LLSD& params)> responder_constructor_t; + +	static void add(const std::string& notification_name, const responder_constructor_t& ctr); + +private: + +	typedef std::map<std::string, responder_constructor_t> build_map_t; + +	static build_map_t sBuildMap; +}; + +////////////////////////////////////////////////////////////////////////// + +LLPersistentNotificationStorage::LLPersistentNotificationStorage() +{ +	mFileName = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" ); +} + +bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload) +{ +	// we ignore "load" messages, but rewrite the persistence file on any other +	const std::string sigtype = payload["sigtype"].asString(); +	if ("load" != sigtype) +	{ +		saveNotifications(); +	} +	return false; +} + +void LLPersistentNotificationStorage::saveNotifications() +{ +	// TODO - think about save optimization. + +	llofstream notify_file(mFileName.c_str()); +	if (!notify_file.is_open()) +	{ +		llwarns << "Failed to open " << mFileName << llendl; +		return; +	} + +	LLSD output; +	LLSD& data = output["data"]; + +	LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent"); +	LLNotificationSet::iterator it = history_channel->begin(); + +	for ( ; history_channel->end() != it; ++it) +	{ +		LLNotificationPtr notification = *it; + +		// After a notification was placed in Persist channel, it can become +		// responded, expired - in this case we are should not save it +		if(notification->isRespondedTo() +			|| notification->isExpired()) +		{ +			continue; +		} + +		data.append(notification->asLLSD()); +	} + +	LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter(); +	formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); +} + +void LLPersistentNotificationStorage::loadNotifications() +{ +	LLResponderRegistry::registerResponders(); + +	LLNotifications::instance().getChannel("Persistent")-> +		connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1)); + +	llifstream notify_file(mFileName.c_str()); +	if (!notify_file.is_open()) +	{ +		llwarns << "Failed to open " << mFileName << llendl; +		return; +	} + +	LLSD input; +	LLPointer<LLSDParser> parser = new LLSDXMLParser(); +	if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0) +	{ +		llwarns << "Failed to parse open notifications" << llendl; +		return; +	} + +	if (input.isUndefined()) +	{ +		return; +	} + +	LLSD& data = input["data"]; +	if (data.isUndefined()) +	{ +		return; +	} + +	using namespace LLNotificationsUI; +	LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()-> +		findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); + +	LLNotifications& instance = LLNotifications::instance(); + +	for (LLSD::array_const_iterator notification_it = data.beginArray(); +		notification_it != data.endArray(); +		++notification_it) +	{ +		LLSD notification_params = *notification_it; +		LLNotificationPtr notification(new LLNotification(notification_params)); + +		LLNotificationResponderPtr responder(LLResponderRegistry:: +			createResponder(notification_params["name"], notification_params["responder"])); +		notification->setResponseFunctor(responder); + +		instance.add(notification); + +		// hide script floaters so they don't confuse the user and don't overlap startup toast +		LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false); + +		if(notification_channel) +		{ +			// hide saved toasts so they don't confuse the user +			notification_channel->hideToast(notification->getID()); +		} +	} +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +LLResponderRegistry::build_map_t LLResponderRegistry::sBuildMap; + +void LLResponderRegistry::registerResponders() +{ +	sBuildMap.clear(); + +	add("ObjectGiveItem", &create<LLOfferInfo>); +	add("UserGiveItem", &create<LLOfferInfo>); +} + +LLNotificationResponderInterface* LLResponderRegistry::createResponder(const std::string& notification_name, const LLSD& params) +{ +	build_map_t::const_iterator it = sBuildMap.find(notification_name); +	if(sBuildMap.end() == it) +	{ +		llwarns << "Responder for notification \'" << notification_name << "\' is not registered" << llendl; +		return NULL; +	} +	responder_constructor_t ctr = it->second; +	return ctr(params); +} + +void LLResponderRegistry::add(const std::string& notification_name, const responder_constructor_t& ctr) +{ +	if(sBuildMap.find(notification_name) != sBuildMap.end()) +	{ +		llwarns << "Responder is already registered : " << notification_name << llendl; +		llassert(!"Responder already registered"); +	} +	sBuildMap[notification_name] = ctr; +} + +// EOF diff --git a/indra/newview/llnotificationstorage.h b/indra/newview/llnotificationstorage.h new file mode 100644 index 0000000000..5050781a85 --- /dev/null +++ b/indra/newview/llnotificationstorage.h @@ -0,0 +1,65 @@ +/** +* @file llnotificationstorage.h +* @brief LLNotificationStorage class declaration +* +* $LicenseInfo:firstyear=2010&license=viewergpl$ +* +* Copyright (c) 2010, 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_NOTIFICATIONSTORAGE_H +#define LL_NOTIFICATIONSTORAGE_H + +#include "llnotifications.h" + +// Class that saves not responded(unread) notifications. +// Unread notifications are saved in open_notifications.xml in SL account folder +// +// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml +// Notifications using functor responders are saved automatically (see llviewermessage.cpp +// lure_callback_reg for example). +// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should +// be a) serializable(implement LLNotificationResponderInterface), +// b) registered with LLResponderRegistry (found in llnotificationstorage.cpp). +class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage> +{ +	LOG_CLASS(LLPersistentNotificationStorage); +public: + +	LLPersistentNotificationStorage(); + +	void saveNotifications(); + +	void loadNotifications(); + +private: + +	bool onPersistentChannelChanged(const LLSD& payload); + +	std::string mFileName; +}; + +#endif // LL_NOTIFICATIONSTORAGE_H diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index 136868a60d..359f252383 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -37,7 +37,7 @@  #include "lltimer.h"  #include "llvoiceclient.h" -struct LLOfferInfo; +class LLOfferInfo;  const S32 UPDATE_MEMBERS_PER_FRAME = 500; diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index e139cb31d6..ae181e2819 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -73,6 +73,9 @@ const U64 WEARABLE_MASK = (1LL << LLInventoryType::IT_WEARABLE);  const U64 ATTACHMENT_MASK = (1LL << LLInventoryType::IT_ATTACHMENT) | (1LL << LLInventoryType::IT_OBJECT);  const U64 ALL_ITEMS_MASK = WEARABLE_MASK | ATTACHMENT_MASK; +static const std::string SAVE_BTN("save_btn"); +static const std::string REVERT_BTN("revert_btn"); +  class LLInventoryLookObserver : public LLInventoryObserver  {  public: @@ -221,10 +224,9 @@ BOOL LLPanelOutfitEdit::postBuild()  	mEditWearableBtn->setVisible(FALSE);  	mEditWearableBtn->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this)); -	childSetAction("revert_btn", boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); +	childSetAction(REVERT_BTN, boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); -	childSetAction("save_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false)); -	childSetAction("save_as_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); +	childSetAction(SAVE_BTN, boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false));  	childSetAction("save_flyout_btn", boost::bind(&LLPanelOutfitEdit::showSaveMenu, this));  	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar save_registar; @@ -234,10 +236,22 @@ BOOL LLPanelOutfitEdit::postBuild()  	mWearableListManager = new LLFilteredWearableListManager(  		getChild<LLInventoryItemsList>("filtered_wearables_list"), ALL_ITEMS_MASK); +		 +	childSetAction("move_closer_btn", boost::bind(&LLPanelOutfitEdit::moveWearable, this, true)); +	childSetAction("move_further_btn", boost::bind(&LLPanelOutfitEdit::moveWearable, this, false));  	return TRUE;  } +void LLPanelOutfitEdit::moveWearable(bool closer_to_body) +{ +	LLViewerInventoryItem* wearable_to_move = gInventory.getItem(mLookContents->getSelectionInterface()->getCurrentID()); +	LLAppearanceMgr::getInstance()->moveWearable(wearable_to_move, closer_to_body); + +	//*TODO why not to listen to inventory? +	updateLookInfo(); +} +  void LLPanelOutfitEdit::showAddWearablesPanel()  {  	childSetVisible("add_wearables_panel", childGetValue("add_btn")); @@ -267,6 +281,8 @@ void LLPanelOutfitEdit::saveOutfit(bool as_new)  	{  		panel_outfits_inventory->onSave();  	} + +	//*TODO how to get to know when base outfit is updated or new outfit is created?  }  void LLPanelOutfitEdit::showSaveMenu() @@ -542,10 +558,12 @@ void LLPanelOutfitEdit::lookFetched(void)  		columns[0]["value"] = item->getName();  		columns[1]["column"] = "look_item_sort";  		columns[1]["type"] = "text"; // TODO: multi-wearable sort "type" should go here. -		columns[1]["value"] = "BAR"; // TODO: Multi-wearable sort index should go here +		columns[1]["value"] = item->LLInventoryItem::getDescription();  		mLookContents->addElement(row);  	} + +	updateVerbs();  }  void LLPanelOutfitEdit::updateLookInfo() @@ -589,4 +607,15 @@ void LLPanelOutfitEdit::displayCurrentOutfit()  	updateLookInfo();  } +//private +void LLPanelOutfitEdit::updateVerbs() +{ +	bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); +	 +	childSetEnabled(SAVE_BTN, outfit_is_dirty); +	childSetEnabled(REVERT_BTN, outfit_is_dirty); + +	mSaveMenu->setItemEnabled("save_outfit", outfit_is_dirty); +} +  // EOF diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 308ee23115..b6f121d484 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -87,6 +87,8 @@ public:  		// Sends a request for data about the given parcel, which will  		// only update the location if there is none already available. +	void moveWearable(bool closer_to_body); +  	void showAddWearablesPanel();  	void showWearablesFilter();  	void showFilteredWearablesPanel(); @@ -110,6 +112,8 @@ public:  private: +	void updateVerbs(); +  	//*TODO got rid of mCurrentOutfitID  	LLUUID				mCurrentOutfitID; diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index f35cb3516a..11b6c0a3ae 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -539,4 +539,14 @@ bool LLScriptFloaterManager::getFloaterPosition(const LLUUID& object_id, Floater  	return false;  } +void LLScriptFloaterManager::setFloaterVisible(const LLUUID& notification_id, bool visible) +{ +	LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>( +		"script_floater", notification_id); +	if(floater) +	{ +		floater->setVisible(visible); +	} +} +  // EOF diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h index ec3ec4b540..dc0cfc2400 100644 --- a/indra/newview/llscriptfloater.h +++ b/indra/newview/llscriptfloater.h @@ -99,6 +99,8 @@ public:  	bool getFloaterPosition(const LLUUID& object_id, FloaterPositionInfo& fpi); +	void setFloaterVisible(const LLUUID& notification_id, bool visible); +  protected:  	typedef std::map<std::string, EObjectType> object_type_map; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 2ea6e5936d..e64696b120 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -850,6 +850,7 @@ bool LLTextureFetchWorker::doWork(S32 param)  				mState = WAIT_HTTP_REQ;	  				mFetcher->addToHTTPQueue(mID); +				mSentRequest = QUEUED;  				// Will call callbackHttpGet when curl request completes  				std::vector<std::string> headers;  				headers.push_back("Accept: image/x-j2c"); @@ -936,6 +937,11 @@ bool LLTextureFetchWorker::doWork(S32 param)  			{  				mFileSize = mBufferSize;  			} +			else //the file size is unknown +			{ +				mFileSize = S32_MAX ; //flag the file is not fully loaded. +			} +  			U8* buffer = new U8[mBufferSize];  			if (cur_size > 0)  			{ diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index c9d2d404c0..089163929e 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -493,7 +493,7 @@ void LLToastNotifyPanel::onClickButton(void* data)  	{  		sButtonClickSignal(self->mNotification->getID(), button_name); -		if(new_info) +		if(new_info && !self->mNotification->isPersistent())  		{  			self->mNotification->setResponseFunctor(  				boost::bind(&LLOfferInfo::inventory_offer_callback, new_info, _1, _2)); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index ec5eb658f6..ba0c8d464d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1252,6 +1252,16 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id,  			gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id));  } +LLOfferInfo::LLOfferInfo() + : LLNotificationResponderInterface() + , mFromGroup(FALSE) + , mFromObject(FALSE) + , mIM(IM_NOTHING_SPECIAL) + , mType(LLAssetType::AT_NONE) + , mPersist(false) +{ +} +  LLOfferInfo::LLOfferInfo(const LLSD& sd)  {  	mIM = (EInstantMessage)sd["im_type"].asInteger(); @@ -1265,6 +1275,7 @@ LLOfferInfo::LLOfferInfo(const LLSD& sd)  	mFromName = sd["from_name"].asString();  	mDesc = sd["description"].asString();  	mHost = LLHost(sd["sender"].asString()); +	mPersist = sd["persist"].asBoolean();  }  LLOfferInfo::LLOfferInfo(const LLOfferInfo& info) @@ -1280,6 +1291,7 @@ LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)  	mFromName = info.mFromName;  	mDesc = info.mDesc;  	mHost = info.mHost; +	mPersist = info.mPersist;  }  LLSD LLOfferInfo::asLLSD() @@ -1296,9 +1308,15 @@ LLSD LLOfferInfo::asLLSD()  	sd["from_name"] = mFromName;  	sd["description"] = mDesc;  	sd["sender"] = mHost.getIPandPort(); +	sd["persist"] = mPersist;  	return sd;  } +void LLOfferInfo::fromLLSD(const LLSD& params) +{ +	*this = params; +} +  void LLOfferInfo::send_auto_receive_response(void)  {	  	LLMessageSystem* msg = gMessageSystem; @@ -1338,6 +1356,21 @@ void LLOfferInfo::send_auto_receive_response(void)  	}  } +void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response) +{ +	initRespondFunctionMap(); + +	const std::string name = notification["name"].asString(); +	if(mRespondFunctions.find(name) == mRespondFunctions.end()) +	{ +		llwarns << "Unexpected notification name : " << name << llendl; +		llassert(!"Unexpected notification name"); +		return; +	} + +	mRespondFunctions[name](notification, response); +} +  bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response)  {  	LLChat chat; @@ -1474,7 +1507,10 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&  		gInventory.addObserver(opener);  	} -	delete this; +	if(!mPersist) +	{ +		delete this; +	}  	return false;  } @@ -1640,7 +1676,10 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const  		gInventory.addObserver(opener);  	} -	delete this; +	if(!mPersist) +	{ +		delete this; +	}  	return false;  } @@ -1656,6 +1695,15 @@ protected:  	}  }; +void LLOfferInfo::initRespondFunctionMap() +{ +	if(mRespondFunctions.empty()) +	{ +		mRespondFunctions["ObjectGiveItem"] = boost::bind(&LLOfferInfo::inventory_task_offer_callback, this, _1, _2); +		mRespondFunctions["UserGiveItem"] = boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2); +	} +} +  void inventory_offer_handler(LLOfferInfo* info)  {  	//Until throttling is implmented, busy mode should reject inventory instead of silently @@ -1772,7 +1820,8 @@ void inventory_offer_handler(LLOfferInfo* info)  		// Inventory Slurls don't currently work for non agent transfers, so only display the object name.  		args["ITEM_SLURL"] = msg;  		// Note: sets inventory_task_offer_callback as the callback -		p.substitutions(args).payload(payload).functor.function(boost::bind(&LLOfferInfo::inventory_task_offer_callback, info, _1, _2)); +		p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); +		info->mPersist = true;  		p.name = name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser";  		// Pop up inv offer chiclet and let the user accept (keep), or reject (and silently delete) the inventory.  		LLNotifications::instance().add(p); @@ -1784,7 +1833,8 @@ void inventory_offer_handler(LLOfferInfo* info)  		// *TODO fix memory leak  		// inventory_offer_callback() is not invoked if user received notification and   		// closes viewer(without responding the notification) -		p.substitutions(args).payload(payload).functor.function(boost::bind(&LLOfferInfo::inventory_offer_callback, info, _1, _2)); +		p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); +		info->mPersist = true;  		p.name = "UserGiveItem";  		// Prefetch the item into your local inventory. diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 7c021dc05f..72ad3c8926 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -40,6 +40,7 @@  #include "lluuid.h"  #include "message.h"  #include "stdenums.h" +#include "llnotifications.h"  //  // Forward declarations @@ -210,11 +211,10 @@ bool highlight_offered_item(const LLUUID& item_id);  void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid); -struct LLOfferInfo +class LLOfferInfo : public LLNotificationResponderInterface  { -        LLOfferInfo() -	:	mFromGroup(FALSE), mFromObject(FALSE), -		mIM(IM_NOTHING_SPECIAL), mType(LLAssetType::AT_NONE) {}; +public: +	LLOfferInfo();  	LLOfferInfo(const LLSD& sd);  	LLOfferInfo(const LLOfferInfo& info); @@ -232,12 +232,27 @@ struct LLOfferInfo  	std::string mFromName;  	std::string mDesc;  	LLHost mHost; +	bool mPersist; + +	// LLNotificationResponderInterface implementation +	/*virtual*/ LLSD asLLSD(); +	/*virtual*/ void fromLLSD(const LLSD& params); +	/*virtual*/ void handleRespond(const LLSD& notification, const LLSD& response); -	LLSD asLLSD();  	void send_auto_receive_response(void); + +	// TODO - replace all references with handleRespond()  	bool inventory_offer_callback(const LLSD& notification, const LLSD& response);  	bool inventory_task_offer_callback(const LLSD& notification, const LLSD& response); +private: + +	void initRespondFunctionMap(); + +	typedef boost::function<bool (const LLSD&, const LLSD&)> respond_function_t; +	typedef std::map<std::string, respond_function_t> respond_function_map_t; + +	respond_function_map_t mRespondFunctions;  };  void process_feature_disabled_message(LLMessageSystem* msg, void**); diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 07304eb14e..5d184fea3a 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5066,6 +5066,7 @@ No valid parcel could be found.    <notification     icon="notify.tga"     name="ObjectGiveItem" +   persist="true"     type="offer">  An object named [OBJECTFROMNAME] owned by [NAME_SLURL] has given you this [OBJECTTYPE]:  [ITEM_SLURL] @@ -5110,6 +5111,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th    <notification     icon="notify.tga"     name="UserGiveItem" +   persist="true"     type="offer">  [NAME_SLURL] has given you this [OBJECTTYPE]:  [ITEM_SLURL] @@ -5162,6 +5164,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th    <notification     icon="notify.tga"     name="TeleportOffered" +   persist="true"     type="offer">  [NAME_SLURL] has offered to teleport you to their location: @@ -5207,6 +5210,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th    <notification     icon="notify.tga"     name="OfferFriendship" +   persist="true"     type="offer">  [NAME_SLURL] is offering friendship. diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index b1f0ff15cb..314d2389ae 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -222,6 +222,30 @@                   top="1"                   width="31" />                  <button +                 follows="bottom|left" +                 height="25" +                 image_hover_unselected="Toolbar_Middle_Over" +                 image_overlay="Movement_Forward_On" +                 image_selected="Toolbar_Middle_Selected" +                 image_unselected="Toolbar_Middle_Off" +                 layout="topleft" +                 left_pad="1" +                 name="move_closer_btn" +                 top="1" +                 width="31" /> +                <button +                 follows="bottom|left" +                 height="25" +                 image_hover_unselected="Toolbar_Middle_Over" +                 image_overlay="Movement_Backward_On" +                 image_selected="Toolbar_Middle_Selected" +                 image_unselected="Toolbar_Middle_Off" +                 layout="topleft" +                 left_pad="1" +                 name="move_further_btn" +                 top="1" +                 width="31" /> +                <button                   follows="bottom|right"                   height="25"                   image_hover_unselected="Toolbar_Middle_Over" | 
