summaryrefslogtreecommitdiff
path: root/indra/newview/llfriendcard.cpp
diff options
context:
space:
mode:
authorSteven Bennetts <steve@lindenlab.com>2009-08-28 22:58:17 +0000
committerSteven Bennetts <steve@lindenlab.com>2009-08-28 22:58:17 +0000
commit0aa2bc4831674e61edf680887eba9acec9f997ed (patch)
treeedf19fd9b857cf13b9e7ad943b65178954b4811c /indra/newview/llfriendcard.cpp
parent51500f82e23b7deff9a0e1bc23f5bfa40aec1fb9 (diff)
svn merge -r 1490-1500 https://svn.aws.productengine.com/secondlife/pe/stable-2
Bug Fixes: * EXT-637 EXT-595 Dev work: * EXT-694 QA: * Be sure feature works on other locales Known issues: * If old Calling Card of person who presents in the friend list is removed - "Do you want to remove ..." dialog is shown, if old Calling Card of non-friend is removed it is removed in the old way without any alerts. * It is possible to create 2 deep level subfolders under the Calling Cards/Friends/ subfolders. This should be disabled due to Accordion does not support tree hierarchy. * If friendship is removed when agent is offline, Friend Card is not removed from Inventory.
Diffstat (limited to 'indra/newview/llfriendcard.cpp')
-rw-r--r--indra/newview/llfriendcard.cpp425
1 files changed, 425 insertions, 0 deletions
diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp
new file mode 100644
index 0000000000..bef5f094e3
--- /dev/null
+++ b/indra/newview/llfriendcard.cpp
@@ -0,0 +1,425 @@
+/**
+ * @file llfriendcard.cpp
+ * @brief Implementation of classes to process Friends Cards
+ *
+ * $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 "llinventory.h"
+#include "lltrans.h"
+
+#include "llfriendcard.h"
+
+#include "llcallingcard.h" // for LLAvatarTracker
+#include "llviewerinventory.h"
+#include "llinventorymodel.h"
+
+// Constants;
+
+static const std::string INVENTORY_STRING_FRIENDS_SUBFOLDER = "Friends";
+static const std::string INVENTORY_STRING_FRIENDS_ALL_SUBFOLDER = "All";
+
+// helper functions
+
+/*
+mantipov *NOTE: unable to use
+LLTrans::getString("InvFolder Friends"); or
+LLTrans::getString("InvFolder FriendsAll");
+in next two functions to set localized folders' names because of there is a hack in the
+LLFolderViewItem::refreshFromListener() method for protected asset types.
+So, localized names will be got from the strings with "InvFolder LABEL_NAME" in the strings.xml
+*/
+inline const std::string& get_friend_folder_name()
+{
+ return INVENTORY_STRING_FRIENDS_SUBFOLDER;
+}
+
+inline const std::string& get_friend_all_subfolder_name()
+{
+ return INVENTORY_STRING_FRIENDS_ALL_SUBFOLDER;
+}
+
+void move_from_to_arrays(LLInventoryModel::cat_array_t& from, LLInventoryModel::cat_array_t& to)
+{
+ while (from.count() > 0)
+ {
+ to.put(from.get(0));
+ from.remove(0);
+ }
+}
+
+const LLUUID& get_folder_uuid(const LLUUID& parentFolderUUID, LLInventoryCollectFunctor& matchFunctor)
+{
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+
+ gInventory.collectDescendentsIf(parentFolderUUID, cats, items,
+ LLInventoryModel::EXCLUDE_TRASH, matchFunctor);
+
+ if (cats.count() == 1)
+ {
+ return cats.get(0)->getUUID();
+ }
+
+ return LLUUID::null;
+}
+
+// LLFriendCardsManager Constructor / Destructor
+LLFriendCardsManager::LLFriendCardsManager()
+{
+ LLAvatarTracker::instance().addObserver(this);
+}
+
+LLFriendCardsManager::~LLFriendCardsManager()
+{
+ LLAvatarTracker::instance().removeObserver(this);
+}
+
+void LLFriendCardsManager::putAvatarData(const LLUUID& avatarID)
+{
+ llinfos << "Store avatar data, avatarID: " << avatarID << llendl;
+ std::pair< avatar_uuid_set_t::iterator, bool > pr;
+ pr = mBuddyIDSet.insert(avatarID);
+ if (pr.second == false)
+ {
+ llwarns << "Trying to add avatar UUID for the stored avatar: "
+ << avatarID
+ << llendl;
+ }
+}
+
+const LLUUID LLFriendCardsManager::extractAvatarID(const LLUUID& avatarID)
+{
+ LLUUID rv;
+ avatar_uuid_set_t::iterator it = mBuddyIDSet.find(avatarID);
+ if (mBuddyIDSet.end() == it)
+ {
+ llwarns << "Call method for non-existent avatar name in the map: " << avatarID << llendl;
+ }
+ else
+ {
+ rv = (*it);
+ mBuddyIDSet.erase(it);
+ }
+ return rv;
+}
+
+// be sure LLInventoryModel::buildParentChildMap() has been called before it.
+// and this method must be called before any actions with friend list
+void LLFriendCardsManager::ensureFriendFoldersExist()
+{
+ LLUUID callingCardsFolderID = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+
+ LLUUID friendFolderUUID = findFriendFolderUUIDImpl();
+
+ if (friendFolderUUID.isNull())
+ {
+ friendFolderUUID = gInventory.createNewCategory(callingCardsFolderID,
+ LLAssetType::AT_CALLINGCARD, get_friend_folder_name());
+ }
+
+ LLUUID friendAllSubfolderUUID = findFriendAllSubfolderUUIDImpl();
+
+ if (friendAllSubfolderUUID.isNull())
+ {
+ friendAllSubfolderUUID = gInventory.createNewCategory(friendFolderUUID,
+ LLAssetType::AT_CALLINGCARD, get_friend_all_subfolder_name());
+ }
+}
+
+
+bool LLFriendCardsManager::isItemInAnyFriendsList(const LLViewerInventoryItem* item)
+{
+ if (item->getType() != LLAssetType::AT_CALLINGCARD)
+ return false;
+
+ LLInventoryModel::item_array_t items;
+ findMatchedFriendCards(item->getCreatorUUID(), items);
+
+ return items.count() > 0;
+}
+
+bool LLFriendCardsManager::isCategoryInFriendFolder(const LLViewerInventoryCategory* cat) const
+{
+ if (NULL == cat)
+ return false;
+ return TRUE == gInventory.isObjectDescendentOf(cat->getUUID(), findFriendFolderUUIDImpl());
+}
+
+void LLFriendCardsManager::syncFriendsFolder()
+{
+ //lets create "Friends" and "Friends/All" in the Inventory "Calling Cards" if they are absent
+ LLFriendCardsManager::instance().ensureFriendFoldersExist();
+
+ LLAvatarTracker::buddy_map_t all_buddies;
+ LLAvatarTracker::instance().copyBuddyList(all_buddies);
+
+ // 1. Remove Friend Cards for non-friends
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+
+ gInventory.collectDescendents(findFriendAllSubfolderUUIDImpl(), cats, items, LLInventoryModel::EXCLUDE_TRASH);
+
+ LLInventoryModel::item_array_t::const_iterator it;
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ lldebugs << "Check if buddy is in list: " << (*it)->getName() << " " << (*it)->getCreatorUUID() << llendl;
+ if (NULL == get_ptr_in_map(all_buddies, (*it)->getCreatorUUID()))
+ {
+ lldebugs << "NONEXISTS, so remove it" << llendl;
+ removeFriendCardFromInventory((*it)->getCreatorUUID());
+ }
+ }
+
+ // 2. Add missing Friend Cards for friends
+ LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin();
+ llinfos << "try to build friends, count: " << all_buddies.size() << llendl;
+ for(; buddy_it != all_buddies.end(); ++buddy_it)
+ {
+ const LLUUID& buddy_id = (*buddy_it).first;
+ addFriendCardToInventory(buddy_id);
+ }
+}
+
+void LLFriendCardsManager::collectFriendsLists(folderid_buddies_map_t& folderBuddiesMap) const
+{
+ folderBuddiesMap.clear();
+
+ LLInventoryModel::cat_array_t* listFolders;
+ LLInventoryModel::item_array_t* items;
+
+ // get folders in the Friend folder. Items should be NULL due to Cards should be in lists.
+ gInventory.getDirectDescendentsOf(findFriendFolderUUIDImpl(), listFolders, items);
+
+ if (NULL == listFolders)
+ return;
+
+ LLInventoryModel::cat_array_t::const_iterator itCats; // to iterate Friend Lists (categories)
+ LLInventoryModel::item_array_t::const_iterator itBuddy; // to iterate Buddies in each List
+ LLInventoryModel::cat_array_t* fakeCatsArg;
+ for (itCats = listFolders->begin(); itCats != listFolders->end(); ++itCats)
+ {
+ if (items)
+ items->clear();
+
+ // *HACK: Only Friends/All content will be shown for now
+ // *TODO: Remove this hack, implement sorting if it will be needded by spec.
+ if ((*itCats)->getUUID() != findFriendAllSubfolderUUIDImpl())
+ continue;
+
+ gInventory.getDirectDescendentsOf((*itCats)->getUUID(), fakeCatsArg, items);
+
+ if (NULL == items)
+ continue;
+
+ std::vector<LLUUID> buddyUUIDs;
+ for (itBuddy = items->begin(); itBuddy != items->end(); ++itBuddy)
+ {
+ buddyUUIDs.push_back((*itBuddy)->getCreatorUUID());
+ }
+
+ folderBuddiesMap.insert(make_pair((*itCats)->getUUID(), buddyUUIDs));
+ }
+}
+
+
+/************************************************************************/
+/* Private Methods */
+/************************************************************************/
+const LLUUID& LLFriendCardsManager::findFriendFolderUUIDImpl() const
+{
+ LLUUID callingCardsFolderID = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+
+ std::string friendFolderName = get_friend_folder_name();
+
+ return findChildFolderUUID(callingCardsFolderID, friendFolderName);
+}
+
+const LLUUID& LLFriendCardsManager::findFriendAllSubfolderUUIDImpl() const
+{
+ LLUUID friendFolderUUID = findFriendFolderUUIDImpl();
+
+ std::string friendAllSubfolderName = get_friend_all_subfolder_name();
+
+ return findChildFolderUUID(friendFolderUUID, friendAllSubfolderName);
+}
+
+const LLUUID& LLFriendCardsManager::findChildFolderUUID(const LLUUID& parentFolderUUID, const std::string& folderLabel) const
+{
+ // mantipov *HACK: get localaized name in the same way like in the LLFolderViewItem::refreshFromListener() method.
+ // be sure these both methods are synchronized.
+ // see also get_friend_folder_name() and get_friend_all_subfolder_name() functions
+ std::string localizedName = LLTrans::getString("InvFolder " + folderLabel);
+
+ LLNameCategoryCollector matchFolderFunctor(localizedName);
+
+ return get_folder_uuid(parentFolderUUID, matchFolderFunctor);
+}
+const LLUUID& LLFriendCardsManager::findFriendCardInventoryUUIDImpl(const LLUUID& avatarID)
+{
+ LLUUID friendAllSubfolderUUID = findFriendAllSubfolderUUIDImpl();
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLInventoryModel::item_array_t::const_iterator it;
+
+ // it is not necessary to check friendAllSubfolderUUID against NULL. It will be processed by collectDescendents
+ gInventory.collectDescendents(friendAllSubfolderUUID, cats, items, LLInventoryModel::EXCLUDE_TRASH);
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ if ((*it)->getCreatorUUID() == avatarID)
+ return (*it)->getUUID();
+ }
+
+ return LLUUID::null;
+}
+
+void LLFriendCardsManager::findMatchedFriendCards(const LLUUID& avatarID, LLInventoryModel::item_array_t& items) const
+{
+ LLInventoryModel::cat_array_t cats;
+ LLUUID friendFolderUUID = findFriendFolderUUIDImpl();
+
+ LLParticularBuddyCollector matchFunctor(avatarID);
+
+ LLViewerInventoryCategory* friendFolder = gInventory.getCategory(friendFolderUUID);
+
+ LLInventoryModel::cat_array_t subFolders;
+ subFolders.push_back(friendFolder);
+
+ while (subFolders.count() > 0)
+ {
+ LLViewerInventoryCategory* cat = subFolders.get(0);
+ subFolders.remove(0);
+
+ gInventory.collectDescendentsIf(cat->getUUID(), cats, items,
+ LLInventoryModel::EXCLUDE_TRASH, matchFunctor);
+
+ move_from_to_arrays(cats, subFolders);
+ }
+}
+
+class CreateFriendCardCallback : public LLInventoryCallback
+{
+public:
+ void fire(const LLUUID& inv_item_id)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(inv_item_id);
+
+ if (item)
+ LLFriendCardsManager::instance().extractAvatarID(item->getCreatorUUID());
+ }
+};
+
+bool LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID)
+{
+ LLInventoryModel* invModel = &gInventory;
+
+ bool shouldBeAdded = true;
+ std::string name;
+ gCacheName->getFullName(avatarID, name);
+
+ lldebugs << "Processing buddy name: " << name
+ << ", id: " << avatarID
+ << llendl;
+
+ if (shouldBeAdded && findFriendCardInventoryUUIDImpl(avatarID).notNull())
+ {
+ shouldBeAdded = false;
+ lldebugs << "is found in Inventory: " << name << llendl;
+ }
+
+ if (shouldBeAdded && isAvatarDataStored(avatarID))
+ {
+ shouldBeAdded = false;
+ lldebugs << "is found in sentRequests: " << name << llendl;
+ }
+
+ LLUUID friendListFolderID = findFriendAllSubfolderUUIDImpl();
+ if (shouldBeAdded && !invModel->isCategoryComplete(friendListFolderID))
+ {
+ shouldBeAdded = false;
+ }
+ if (shouldBeAdded)
+ {
+ putAvatarData(avatarID);
+ lldebugs << "Sent create_inventory_item for " << avatarID << ", " << name << llendl;
+
+ // TODO: mantipov: Is CreateFriendCardCallback really needed? Probably not
+ LLPointer<LLInventoryCallback> cb = new CreateFriendCardCallback();
+
+ create_inventory_callingcard(avatarID, friendListFolderID, cb);
+ }
+
+ return shouldBeAdded;
+}
+
+void LLFriendCardsManager::removeFriendCardFromInventory(const LLUUID& avatarID)
+{
+ LLInventoryModel::item_array_t items;
+ findMatchedFriendCards(avatarID, items);
+
+ LLInventoryModel::item_array_t::const_iterator it;
+ for (it = items.begin(); it != items.end(); ++ it)
+ {
+ gInventory.removeItem((*it)->getUUID());
+ }
+}
+
+void LLFriendCardsManager::onFriendListUpdate(U32 changed_mask)
+{
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+
+ switch(changed_mask) {
+ case LLFriendObserver::ADD:
+ {
+ const std::set<LLUUID>& changed_items = at.getChangedIDs();
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ LLFriendCardsManager::instance().addFriendCardToInventory(*id_it);
+ }
+ }
+ break;
+ case LLFriendObserver::REMOVE:
+ {
+ const std::set<LLUUID>& changed_items = at.getChangedIDs();
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ LLFriendCardsManager::instance().removeFriendCardFromInventory(*id_it);
+ }
+ }
+
+ default:;
+ }
+}
+
+// EOF