Commits

Andrew Productengine  committed da9ee8d

STORM-34 FIXED Saving of user's favorites into file and showing them in "Start at" combobox on login screen was implemented.

Implementation details:

- File is saved on exit from viewer and not immediately on changes as was written in spec. It is done to make this file consistent with favorites order: order of favorites is saved on exit,
so if favorites info is saved in other moment earlier, crashing viewer or other unexpected way of finishing its work (i.e. via Windows task bar) would cause inconsistence between favorites order
saved per account and one from this new file.

- File is saved in user_settings\stored_favorites.xml.

- If you uncheck the option in Preferences and press OK, the file gets immediately deleted (according to spec).

Issues that require further changes:

- Currently only favorites of last logged in user are shown in login screen. Showing favorites of multiple users will be implemented later when design for it is approved by Esbee.

- Preference is now global for all users, because design states it may be changed before login, and we don't have account info at the moment. But it doesn't seem to be a good idea, so changes in design are needed.

- Currently the way of retrieving SLURLs needs optimization in a separate ticket.

More detailed design approved by Esbee is needed to develop it further, perhaps in new tickets.

  • Participants
  • Parent commits e843e27

Comments (0)

Files changed (9)

File indra/newview/app_settings/settings.xml

       <key>Value</key>
       <string>name</string>
     </map>
+    <key>ShowFavoritesOnLogin</key>
+    <map>
+      <key>Comment</key>
+      <string>Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
 </map>
 </llsd>

File indra/newview/llfavoritesbar.cpp

 	}	
 	else
 	{
+		LLInventoryModel::item_array_t items;
+		LLInventoryModel::cat_array_t cats;
+		LLIsType is_type(LLAssetType::AT_LANDMARK);
+		gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type);
+		
+		S32 sortField = 0;
+		for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
+		{
+			(*i)->setSortField(++sortField);
+		}
 		updateButtons();
 	}
 }

File indra/newview/llfloaterpreference.cpp

 	gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));	
 	gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));	
 	gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged,  _2));
+
+	mFavoritesFileMayExist = gSavedSettings.getBOOL("ShowFavoritesOnLogin");
 }
 
 BOOL LLFloaterPreference::postBuild()
 		updateDoubleClickSettings();
 		mDoubleClickActionDirty = false;
 	}
+
+	if (mFavoritesFileMayExist && !gSavedSettings.getBOOL("ShowFavoritesOnLogin"))
+	{
+		mFavoritesFileMayExist = false;
+		std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml");
+		LLFile::remove(filename);
+	}
 }
 
 void LLFloaterPreference::cancel()
 	{
 		getChild<LLCheckBoxCtrl>("voice_call_friends_only_check")->setCommitCallback(boost::bind(&showFriendsOnlyWarning, _1, _2));
 	}
+	if (hasChild("favorites_on_login_check"))
+	{
+		getChild<LLCheckBoxCtrl>("favorites_on_login_check")->setCommitCallback(boost::bind(&showFavoritesOnLoginWarning, _1, _2));
+	}
 
 	// Panel Advanced
 	if (hasChild("modifier_combo"))
 	}
 }
 
+void LLPanelPreference::showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value)
+{
+	if (checkbox && checkbox->getValue())
+	{
+		LLNotificationsUtil::add("FavoritesOnLogin");
+	}
+}
+
 void LLPanelPreference::cancel()
 {
 	for (control_values_map_t::iterator iter =  mSavedValues.begin();

File indra/newview/llfloaterpreference.h

 	bool mLanguageChanged;
 	
 	bool mOriginalHideOnlineStatus;
+	bool mFavoritesFileMayExist;
 	std::string mDirectoryVisibility;
 };
 
 private:
 	//for "Only friends and groups can call or IM me"
 	static void showFriendsOnlyWarning(LLUICtrl*, const LLSD&);
+	//for "Show my Favorite Landmarks at Login"
+	static void showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value);
 
 	typedef std::map<LLControlVariable*, LLSD> control_values_map_t;
 	control_values_map_t mSavedValues;

File indra/newview/llpanellogin.cpp

 	gResponsePtr = LLIamHereLogin::build( this );
 
 	LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr );
-	
+
+	// Show last logged in user favorites in "Start at" combo if corresponding option is enabled.
+	if (gSavedSettings.getBOOL("ShowFavoritesOnLogin"))
+	{
+		addFavoritesToStartLocation();
+	}
+
 	updateLocationCombo(false);
 
 }
 
+void LLPanelLogin::addFavoritesToStartLocation()
+{
+	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml");
+	LLSD fav_llsd;
+	llifstream file;
+	file.open(filename);
+	if (!file.is_open()) return;
+	LLComboBox* combo = getChild<LLComboBox>("start_location_combo");
+	combo->addSeparator();
+	LLSDSerialize::fromXML(fav_llsd, file);
+	for (LLSD::map_const_iterator iter = fav_llsd.beginMap();
+		iter != fav_llsd.endMap(); ++iter)
+	{
+		LLSD user_llsd = iter->second;
+		for (LLSD::array_const_iterator iter1 = user_llsd.beginArray();
+			iter1 != user_llsd.endArray(); ++iter1)
+		{
+			std::string label = (*iter1)["name"].asString();
+			std::string value = (*iter1)["slurl"].asString();
+			if(label != "" && value != "")
+			{
+				combo->add(label, value);
+			}
+		}
+
+	}
+}
+
 // force the size to be correct (XML doesn't seem to be sufficient to do this)
 // (with some padding so the other login screen doesn't show through)
 void LLPanelLogin::reshapeBrowser()

File indra/newview/llpanellogin.h

 private:
 	friend class LLPanelLoginListener;
 	void reshapeBrowser();
+	// adds favorites of last logged in user from file to "Start at" combobox.
+	void addFavoritesToStartLocation();
 	static void onClickConnect(void*);
 	static void onClickNewAccount(void*);
 //	static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response);

File indra/newview/llviewerinventory.cpp

 #include "llinventorybridge.h"
 #include "llinventorypanel.h"
 #include "llfloaterinventory.h"
+#include "lllandmarkactions.h"
 
 #include "llviewerassettype.h"
 #include "llviewerregion.h"
 #include "llcommandhandler.h"
 #include "llviewermessage.h"
 #include "llsidepanelappearance.h"
+#include "llavatarnamecache.h"
+#include "lllogininstance.h"
 
 ///----------------------------------------------------------------------------
 /// Helper class to store special inventory item names and their localized values.
 	S32 getSortIndex(const LLUUID& inv_item_id);
 	void removeSortIndex(const LLUUID& inv_item_id);
 
+	void getSLURL(const LLUUID& asset_id);
+
 	/**
 	 * Implementation of LLDestroyClass. Calls cleanup() instance method.
 	 *
 	void load();
 	void save();
 
+	void saveFavoritesSLURLs();
+
+	void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark);
+	void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl);
+
 	typedef std::map<LLUUID, S32> sort_index_map_t;
 	sort_index_map_t mSortIndexes;
 
+	typedef std::map<LLUUID, std::string> slurls_map_t;
+	slurls_map_t mSLURLs;
+
 	bool mIsDirty;
 
 	struct IsNotInFavorites
 	mIsDirty = true;
 }
 
+void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id)
+{
+	slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id);
+	if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached
+
+	LLLandmark* lm = gLandmarkList.getAsset(asset_id,
+			boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1));
+	if (lm)
+	{
+		onLandmarkLoaded(asset_id, lm);
+	}
+}
+
 // static
 void LLFavoritesOrderStorage::destroyClass()
 {
 	LLFavoritesOrderStorage::instance().cleanup();
+	if (gSavedSettings.getBOOL("ShowFavoritesOnLogin"))
+	{
+		LLFavoritesOrderStorage::instance().saveFavoritesSLURLs();
+	}
 }
 
 void LLFavoritesOrderStorage::load()
 	}
 }
 
+void LLFavoritesOrderStorage::saveFavoritesSLURLs()
+{
+	// Do not change the file if we are not logged in yet.
+	if (!LLLoginInstance::getInstance()->authSuccess()) return;
+	
+	std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "");
+	if (user_dir.empty()) return;
+
+	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml");
+
+	const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH);
+
+	LLSD user_llsd;
+	for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++)
+	{
+		LLSD value;
+		value["name"] = (*it)->getName();
+		value["asset_id"] = (*it)->getAssetUUID();
+
+		slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]);
+		if (slurl_iter != mSLURLs.end())
+		{
+			value["slurl"] = slurl_iter->second;
+		}
+		else
+		{
+			llwarns << "Fetching SLURLs for \"Favorites\" is not complete!" << llendl;
+			return;
+		}
+
+		user_llsd[(*it)->getSortField()] = value;
+	}
+
+	LLSD fav_llsd;
+	// this level in LLSD is not needed now and is just a stub, but will be needed later when implementing save of multiple users favorites in one file.
+	LLAvatarName av_name;
+	LLAvatarNameCache::get( gAgentID, &av_name );
+	fav_llsd[av_name.getLegacyName()] = user_llsd;
+
+	llofstream file;
+	file.open(filename);
+	LLSDSerialize::toPrettyXML(fav_llsd, file);
+}
+
+void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark)
+{
+	if (!landmark) return;
+
+	LLVector3d pos_global;
+	if (!landmark->getGlobalPos(pos_global))
+	{
+		// If global position was unknown on first getGlobalPos() call
+		// it should be set for the subsequent calls.
+		landmark->getGlobalPos(pos_global);
+	}
+
+	if (!pos_global.isExactlyZero())
+	{
+		LLLandmarkActions::getSLURLfromPosGlobal(pos_global,
+				boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1));
+	}
+}
+
+void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl)
+{
+	mSLURLs[asset_id] = slurl;
+}
+
 void LLFavoritesOrderStorage::save()
 {
 	// nothing to save if clean
 void LLViewerInventoryItem::setSortField(S32 sortField)
 {
 	LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField);
+	LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID);
 }
 
 const LLPermissions& LLViewerInventoryItem::getPermissions() const

File indra/newview/skins/default/xui/en/notifications.xml

 
   <notification
    icon="alertmodal.tga"
+   name="FavoritesOnLogin"
+   type="alertmodal">    
+    Note: When you turn on this option, anyone who uses this computer can see your list of favorite locations.
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
    name="GrantModifyRights"
    type="alertmodal">
 Granting modify rights to another Resident allows them to change, delete or take ANY objects you may have in-world. Be VERY careful when handing out this permission.

File indra/newview/skins/default/xui/en/panel_preferences_privacy.xml

      name="auto_disengage_mic_check"
      top_pad="10"
      width="350" />
+    <check_box
+     control_name="ShowFavoritesOnLogin"
+     height="16"
+     label="Show my Favorite Landmarks at Login (via &apos;Start At&apos; drop-down menu)"
+     layout="topleft"
+     left="30"
+     name="favorites_on_login_check"
+     top_pad="10"
+     width="350" />
 	<text
       type="string"
     length="1"
      left="30"
      mouse_opaque="false"
      name="Logs:"
-     top_pad="30"
+     top_pad="20"
      width="350">
         Chat Logs:
     </text>
      layout="topleft"
      left="30"
      name="block_list"
-     top_pad="35"
+     top_pad="28"
      width="145">
         <!--<button.commit_callback
          function="SideTray.ShowPanel"-->