diff options
Diffstat (limited to 'indra/newview/llinventorybridge.cpp')
-rw-r--r-- | indra/newview/llinventorybridge.cpp | 16524 |
1 files changed, 8262 insertions, 8262 deletions
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index e202547a67..76323be3dc 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1,8262 +1,8262 @@ -/**
- * @file llinventorybridge.cpp
- * @brief Implementation of the Inventory-Folder-View-Bridge classes.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llinventorybridge.h"
-
-// external projects
-#include "lltransfersourceasset.h"
-#include "llavatarnamecache.h" // IDEVO
-
-#include "llagent.h"
-#include "llagentcamera.h"
-#include "llagentwearables.h"
-#include "llappearancemgr.h"
-#include "llattachmentsmgr.h"
-#include "llavataractions.h"
-#include "llfavoritesbar.h" // management of favorites folder
-#include "llfloateropenobject.h"
-#include "llfloaterreg.h"
-#include "llfloatermarketplacelistings.h"
-#include "llfloatersidepanelcontainer.h"
-#include "llsidepanelinventory.h"
-#include "llfloaterworldmap.h"
-#include "llfolderview.h"
-#include "llfriendcard.h"
-#include "llgesturemgr.h"
-#include "llgiveinventory.h"
-#include "llfloaterimcontainer.h"
-#include "llimview.h"
-#include "llclipboard.h"
-#include "llinventorydefines.h"
-#include "llinventoryfunctions.h"
-#include "llinventoryicon.h"
-#include "llinventorymodel.h"
-#include "llinventorymodelbackgroundfetch.h"
-#include "llinventorypanel.h"
-#include "llmarketplacefunctions.h"
-#include "llnotifications.h"
-#include "llnotificationsutil.h"
-#include "llpreviewanim.h"
-#include "llpreviewgesture.h"
-#include "llpreviewtexture.h"
-#include "llselectmgr.h"
-#include "llsidepanelappearance.h"
-#include "lltooldraganddrop.h"
-#include "lltrans.h"
-#include "llurlaction.h"
-#include "llviewerassettype.h"
-#include "llviewerfoldertype.h"
-#include "llviewermenu.h"
-#include "llviewermessage.h"
-#include "llviewerobjectlist.h"
-#include "llviewerregion.h"
-#include "llviewerwindow.h"
-#include "llvoavatarself.h"
-#include "llwearablelist.h"
-#include "llwearableitemslist.h"
-#include "lllandmarkactions.h"
-#include "llpanellandmarks.h"
-#include "llviewerparcelmgr.h"
-#include "llparcel.h"
-
-#include "llenvironment.h"
-
-#include <boost/shared_ptr.hpp>
-
-void copy_slurl_to_clipboard_callback_inv(const std::string& slurl);
-
-const F32 SOUND_GAIN = 1.0f;
-const F32 FOLDER_LOADING_MESSAGE_DELAY = 0.5f; // Seconds to wait before showing the LOADING... text in folder views
-
-using namespace LLOldEvents;
-
-// Function declarations
-bool confirm_attachment_rez(const LLSD& notification, const LLSD& response);
-void teleport_via_landmark(const LLUUID& asset_id);
-static bool check_category(LLInventoryModel* model,
- const LLUUID& cat_id,
- LLInventoryPanel* active_panel,
- LLInventoryFilter* filter);
-static bool check_item(const LLUUID& item_id,
- LLInventoryPanel* active_panel,
- LLInventoryFilter* filter);
-
-// Helper functions
-
-bool isAddAction(const std::string& action)
-{
- return ("wear" == action || "attach" == action || "activate" == action);
-}
-
-bool isRemoveAction(const std::string& action)
-{
- return ("take_off" == action || "detach" == action);
-}
-
-bool isMarketplaceSendAction(const std::string& action)
-{
- return ("send_to_marketplace" == action);
-}
-
-bool isPanelActive(const std::string& panel_name)
-{
- LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(false);
- return (active_panel && (active_panel->getName() == panel_name));
-}
-
-// Used by LLFolderBridge as callback for directory fetching recursion
-class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
-{
-public:
- LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids) : LLInventoryFetchDescendentsObserver(ids) {}
- ~LLRightClickInventoryFetchDescendentsObserver() {}
- virtual void execute(bool clear_observer = false);
- virtual void done()
- {
- execute(true);
- }
-};
-
-// Used by LLFolderBridge as callback for directory content items fetching
-class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver
-{
-public:
- LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : LLInventoryFetchItemsObserver(ids) { };
- ~LLRightClickInventoryFetchObserver() {}
- void execute(bool clear_observer = false)
- {
- if (clear_observer)
- {
- gInventory.removeObserver(this);
- delete this;
- }
- // we've downloaded all the items, so repaint the dialog
- LLFolderBridge::staticFolderOptionsMenu();
- }
- virtual void done()
- {
- execute(true);
- }
-};
-
-class LLPasteIntoFolderCallback: public LLInventoryCallback
-{
-public:
- LLPasteIntoFolderCallback(LLHandle<LLInventoryPanel>& handle)
- : mInventoryPanel(handle)
- {
- }
- ~LLPasteIntoFolderCallback()
- {
- processItems();
- }
-
- void fire(const LLUUID& inv_item)
- {
- mChangedIds.push_back(inv_item);
- }
-
- void processItems()
- {
- LLInventoryPanel* panel = mInventoryPanel.get();
- bool has_elements = false;
- for (LLUUID& inv_item : mChangedIds)
- {
- LLInventoryItem* item = gInventory.getItem(inv_item);
- if (item && panel)
- {
- LLUUID root_id = panel->getRootFolderID();
-
- if (inv_item == root_id)
- {
- return;
- }
-
- LLFolderViewItem* item = panel->getItemByID(inv_item);
- if (item)
- {
- if (!has_elements)
- {
- panel->clearSelection();
- panel->getRootFolder()->clearSelection();
- panel->getRootFolder()->requestArrange();
- panel->getRootFolder()->update();
- has_elements = true;
- }
- panel->getRootFolder()->changeSelection(item, true);
- }
- }
- }
-
- if (has_elements)
- {
- panel->getRootFolder()->scrollToShowSelection();
- }
- }
-private:
- LLHandle<LLInventoryPanel> mInventoryPanel;
- std::vector<LLUUID> mChangedIds;
-};
-
-// +=================================================+
-// | LLInvFVBridge |
-// +=================================================+
-
-LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid) :
- mUUID(uuid),
- mRoot(root),
- mInvType(LLInventoryType::IT_NONE),
- mIsLink(false),
- LLFolderViewModelItemInventory(inventory->getRootViewModel())
-{
- mInventoryPanel = inventory->getInventoryPanelHandle();
- const LLInventoryObject* obj = getInventoryObject();
- mIsLink = obj && obj->getIsLinkType();
-}
-
-const std::string& LLInvFVBridge::getName() const
-{
- const LLInventoryObject* obj = getInventoryObject();
- if(obj)
- {
- return obj->getName();
- }
- return LLStringUtil::null;
-}
-
-const std::string& LLInvFVBridge::getDisplayName() const
-{
- if(mDisplayName.empty())
- {
- buildDisplayName();
- }
- return mDisplayName;
-}
-
-std::string LLInvFVBridge::getSearchableDescription() const
-{
- return get_searchable_description(getInventoryModel(), mUUID);
-}
-
-std::string LLInvFVBridge::getSearchableCreatorName() const
-{
- return get_searchable_creator_name(getInventoryModel(), mUUID);
-}
-
-std::string LLInvFVBridge::getSearchableUUIDString() const
-{
- return get_searchable_UUID(getInventoryModel(), mUUID);
-}
-
-// Folders have full perms
-PermissionMask LLInvFVBridge::getPermissionMask() const
-{
- return PERM_ALL;
-}
-
-// virtual
-LLFolderType::EType LLInvFVBridge::getPreferredType() const
-{
- return LLFolderType::FT_NONE;
-}
-
-
-// Folders don't have creation dates.
-time_t LLInvFVBridge::getCreationDate() const
-{
- LLInventoryObject* objectp = getInventoryObject();
- if (objectp)
- {
- return objectp->getCreationDate();
- }
- return (time_t)0;
-}
-
-void LLInvFVBridge::setCreationDate(time_t creation_date_utc)
-{
- LLInventoryObject* objectp = getInventoryObject();
- if (objectp)
- {
- objectp->setCreationDate(creation_date_utc);
- }
-}
-
-
-// Can be destroyed (or moved to trash)
-bool LLInvFVBridge::isItemRemovable(bool check_worn) const
-{
- return get_is_item_removable(getInventoryModel(), mUUID, check_worn);
-}
-
-// Can be moved to another folder
-bool LLInvFVBridge::isItemMovable() const
-{
- return true;
-}
-
-bool LLInvFVBridge::isLink() const
-{
- return mIsLink;
-}
-
-bool LLInvFVBridge::isLibraryItem() const
-{
- return gInventory.isObjectDescendentOf(getUUID(),gInventory.getLibraryRootFolderID());
-}
-
-/*virtual*/
-/**
- * @brief Adds this item into clipboard storage
- */
-bool LLInvFVBridge::cutToClipboard()
-{
- const LLInventoryObject* obj = gInventory.getObject(mUUID);
- if (obj && isItemMovable() && isItemRemovable())
- {
- const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const bool cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id);
-
- if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) ||
- LLMarketplaceData::instance().isListedAndActive(mUUID)))
- {
- LLUUID parent_uuid = obj->getParentUUID();
- bool result = perform_cutToClipboard();
- gInventory.addChangedMask(LLInventoryObserver::STRUCTURE, parent_uuid);
- return result;
- }
- else
- {
- // Otherwise just perform the cut
- return perform_cutToClipboard();
- }
- }
- return false;
-}
-
-// virtual
-bool LLInvFVBridge::isCutToClipboard()
-{
- if (LLClipboard::instance().isCutMode())
- {
- return LLClipboard::instance().isOnClipboard(mUUID);
- }
- return false;
-}
-
-// Callback for cutToClipboard if DAMA required...
-bool LLInvFVBridge::callback_cutToClipboard(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option == 0) // YES
- {
- return perform_cutToClipboard();
- }
- return false;
-}
-
-bool LLInvFVBridge::perform_cutToClipboard()
-{
- const LLInventoryObject* obj = gInventory.getObject(mUUID);
- if (obj && isItemMovable() && isItemRemovable())
- {
- LLClipboard::instance().setCutMode(true);
- return LLClipboard::instance().addToClipboard(mUUID);
- }
- return false;
-}
-
-bool LLInvFVBridge::copyToClipboard() const
-{
- const LLInventoryObject* obj = gInventory.getObject(mUUID);
- if (obj && isItemCopyable())
- {
- return LLClipboard::instance().addToClipboard(mUUID);
- }
- return false;
-}
-
-void LLInvFVBridge::showProperties()
-{
- if (isMarketplaceListingsFolder())
- {
- LLFloaterReg::showInstance("item_properties", LLSD().with("id",mUUID),true);
- // Force it to show on top as this floater has a tendency to hide when confirmation dialog shows up
- LLFloater* floater_properties = LLFloaterReg::findInstance("item_properties", LLSD().with("id",mUUID));
- if (floater_properties)
- {
- floater_properties->setVisibleAndFrontmost();
- }
- }
- else
- {
- show_item_profile(mUUID);
- }
-}
-
-void LLInvFVBridge::navigateToFolder(bool new_window, bool change_mode)
-{
- if(new_window)
- {
- mInventoryPanel.get()->openSingleViewInventory(mUUID);
- }
- else
- {
- if(change_mode)
- {
- LLInventoryPanel::setSFViewAndOpenFolder(mInventoryPanel.get(), mUUID);
- }
- else
- {
- LLInventorySingleFolderPanel* panel = dynamic_cast<LLInventorySingleFolderPanel*>(mInventoryPanel.get());
- if (!panel || !getInventoryModel() || mUUID.isNull())
- {
- return;
- }
-
- panel->changeFolderRoot(mUUID);
- }
-
- }
-}
-
-void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)
-{
- // Deactivate gestures when moving them into Trash
- LLInvFVBridge* bridge;
- LLInventoryModel* model = getInventoryModel();
- LLViewerInventoryItem* item = NULL;
- LLViewerInventoryCategory* cat = NULL;
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- S32 count = batch.size();
- S32 i,j;
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch[i]);
- if(!bridge || !bridge->isItemRemovable()) continue;
- item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
- if (item)
- {
- if(LLAssetType::AT_GESTURE == item->getType())
- {
- LLGestureMgr::instance().deactivateGesture(item->getUUID());
- }
- }
- }
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch[i]);
- if(!bridge || !bridge->isItemRemovable()) continue;
- cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
- if (cat)
- {
- gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, false );
- for (j=0; j<descendent_items.size(); j++)
- {
- if(LLAssetType::AT_GESTURE == descendent_items[j]->getType())
- {
- LLGestureMgr::instance().deactivateGesture(descendent_items[j]->getUUID());
- }
- }
- }
- }
- removeBatchNoCheck(batch);
- model->checkTrashOverflow();
-}
-
-void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch)
-{
- // this method moves a bunch of items and folders to the trash. As
- // per design guidelines for the inventory model, the message is
- // built and the accounting is performed first. After all of that,
- // we call LLInventoryModel::moveObject() to move everything
- // around.
- LLInvFVBridge* bridge;
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLMessageSystem* msg = gMessageSystem;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- LLViewerInventoryItem* item = NULL;
- uuid_vec_t move_ids;
- LLInventoryModel::update_map_t update;
- bool start_new_message = true;
- S32 count = batch.size();
- S32 i;
-
- // first, hide any 'preview' floaters that correspond to the items
- // being deleted.
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch[i]);
- if(!bridge || !bridge->isItemRemovable()) continue;
- item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
- if(item)
- {
- LLPreview::hide(item->getUUID());
- }
- }
-
- // do the inventory move to trash
-
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch[i]);
- if(!bridge || !bridge->isItemRemovable()) continue;
- item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
- if(item)
- {
- if(item->getParentUUID() == trash_id) continue;
- move_ids.push_back(item->getUUID());
- --update[item->getParentUUID()];
- ++update[trash_id];
- if(start_new_message)
- {
- start_new_message = false;
- msg->newMessageFast(_PREHASH_MoveInventoryItem);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addBOOLFast(_PREHASH_Stamp, true);
- }
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
- msg->addUUIDFast(_PREHASH_FolderID, trash_id);
- msg->addString("NewName", NULL);
- if(msg->isSendFullFast(_PREHASH_InventoryData))
- {
- start_new_message = true;
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- update.clear();
- }
- }
- }
- if(!start_new_message)
- {
- start_new_message = true;
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- update.clear();
- }
-
- for(i = 0; i < count; ++i)
- {
- bridge = (LLInvFVBridge*)(batch[i]);
- if(!bridge || !bridge->isItemRemovable()) continue;
- LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
- if(cat)
- {
- if(cat->getParentUUID() == trash_id) continue;
- move_ids.push_back(cat->getUUID());
- --update[cat->getParentUUID()];
- ++update[trash_id];
- if(start_new_message)
- {
- start_new_message = false;
- msg->newMessageFast(_PREHASH_MoveInventoryFolder);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->addBOOL("Stamp", true);
- }
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID());
- msg->addUUIDFast(_PREHASH_ParentID, trash_id);
- if(msg->isSendFullFast(_PREHASH_InventoryData))
- {
- start_new_message = true;
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- update.clear();
- }
- }
- }
- if(!start_new_message)
- {
- gAgent.sendReliableMessage();
- gInventory.accountForUpdate(update);
- }
-
- // move everything.
- uuid_vec_t::iterator it = move_ids.begin();
- uuid_vec_t::iterator end = move_ids.end();
- for(; it != end; ++it)
- {
- gInventory.moveObject((*it), trash_id);
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if (item)
- {
- model->updateItem(item);
- }
- }
-
- // notify inventory observers.
- model->notifyObservers();
-}
-
-bool LLInvFVBridge::isClipboardPasteable() const
-{
- // Return false on degenerated cases: empty clipboard, no inventory, no agent
- if (!LLClipboard::instance().hasContents() || !isAgentInventory())
- {
- return false;
- }
- LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return false;
- }
-
- // In cut mode, whatever is on the clipboard is always pastable
- if (LLClipboard::instance().isCutMode())
- {
- return true;
- }
-
- // In normal mode, we need to check each element of the clipboard to know if we can paste or not
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
- S32 count = objects.size();
- for(S32 i = 0; i < count; i++)
- {
- const LLUUID &item_id = objects.at(i);
-
- // Folders are pastable if all items in there are copyable
- const LLInventoryCategory *cat = model->getCategory(item_id);
- if (cat)
- {
- LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, item_id);
- if (!cat_br.isItemCopyable(false))
- return false;
- // Skip to the next item in the clipboard
- continue;
- }
-
- // Each item must be copyable to be pastable
- LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id);
- if (!item_br.isItemCopyable(false))
- {
- return false;
- }
- }
- return true;
-}
-
-bool LLInvFVBridge::isClipboardPasteableAsLink() const
-{
- if (!LLClipboard::instance().hasContents() || !isAgentInventory())
- {
- return false;
- }
- const LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return false;
- }
-
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
- S32 count = objects.size();
- for(S32 i = 0; i < count; i++)
- {
- const LLInventoryItem *item = model->getItem(objects.at(i));
- if (item)
- {
- if (!LLAssetType::lookupCanLink(item->getActualType()))
- {
- return false;
- }
-
- if (gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getLibraryRootFolderID()))
- {
- return false;
- }
- }
- const LLViewerInventoryCategory *cat = model->getCategory(objects.at(i));
- if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
- {
- return false;
- }
- }
- return true;
-}
-
-void disable_context_entries_if_present(LLMenuGL& menu,
- const menuentry_vec_t &disabled_entries)
-{
- const LLView::child_list_t *list = menu.getChildList();
- for (LLView::child_list_t::const_iterator itor = list->begin();
- itor != list->end();
- ++itor)
- {
- LLView *menu_item = (*itor);
- std::string name = menu_item->getName();
-
- // descend into split menus:
- LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item);
- if ((name == "More") && branchp)
- {
- disable_context_entries_if_present(*branchp->getBranch(), disabled_entries);
- }
-
- bool found = false;
- menuentry_vec_t::const_iterator itor2;
- for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
- {
- if (*itor2 == name)
- {
- found = true;
- break;
- }
- }
-
- if (found)
- {
- menu_item->setVisible(true);
- // A bit of a hack so we can remember that some UI element explicitly set this to be visible
- // so that some other UI element from multi-select doesn't later set this invisible.
- menu_item->pushVisible(true);
-
- menu_item->setEnabled(false);
- }
- }
-}
-void hide_context_entries(LLMenuGL& menu,
- const menuentry_vec_t &entries_to_show,
- const menuentry_vec_t &disabled_entries)
-{
- const LLView::child_list_t *list = menu.getChildList();
-
- // For removing double separators or leading separator. Start at true so that
- // if the first element is a separator, it will not be shown.
- bool is_previous_entry_separator = true;
-
- for (LLView::child_list_t::const_iterator itor = list->begin();
- itor != list->end();
- ++itor)
- {
- LLView *menu_item = (*itor);
- std::string name = menu_item->getName();
-
- // descend into split menus:
- LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item);
- if (((name == "More") || (name == "create_new")) && branchp)
- {
- hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries);
- }
-
- bool found = false;
-
- menuentry_vec_t::const_iterator itor2 = std::find(entries_to_show.begin(), entries_to_show.end(), name);
- if (itor2 != entries_to_show.end())
- {
- found = true;
- }
-
- // Don't allow multiple separators in a row (e.g. such as if there are no items
- // between two separators).
- if (found)
- {
- const bool is_entry_separator = (dynamic_cast<LLMenuItemSeparatorGL *>(menu_item) != NULL);
- found = !(is_entry_separator && is_previous_entry_separator);
- is_previous_entry_separator = is_entry_separator;
- }
-
- if (!found)
- {
- if (!menu_item->getLastVisible())
- {
- menu_item->setVisible(false);
- }
-
- if (menu_item->getEnabled())
- {
- // These should stay enabled unless specifically disabled
- const menuentry_vec_t exceptions = {
- "Detach From Yourself",
- "Wearable And Object Wear",
- "Wearable Add",
- };
-
- menuentry_vec_t::const_iterator itor2 = std::find(exceptions.begin(), exceptions.end(), name);
- if (itor2 == exceptions.end())
- {
- menu_item->setEnabled(false);
- }
- }
- }
- else
- {
- menu_item->setVisible(true);
- // A bit of a hack so we can remember that some UI element explicitly set this to be visible
- // so that some other UI element from multi-select doesn't later set this invisible.
- menu_item->pushVisible(true);
-
- bool enabled = true;
- for (itor2 = disabled_entries.begin(); enabled && (itor2 != disabled_entries.end()); ++itor2)
- {
- enabled &= (*itor2 != name);
- }
-
- menu_item->setEnabled(enabled);
- }
- }
-}
-
-// Helper for commonly-used entries
-void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
- menuentry_vec_t &items,
- menuentry_vec_t &disabled_items, U32 flags)
-{
- const LLInventoryObject *obj = getInventoryObject();
- bool single_folder_root = (mRoot == NULL);
-
- if (obj)
- {
-
- if (obj->getType() != LLAssetType::AT_CATEGORY)
- {
- items.push_back(std::string("Copy Separator"));
- }
- items.push_back(std::string("Copy"));
- if (!isItemCopyable())
- {
- disabled_items.push_back(std::string("Copy"));
- }
-
- if (isAgentInventory() && !single_folder_root)
- {
- items.push_back(std::string("New folder from selected"));
- items.push_back(std::string("Subfolder Separator"));
- std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs();
- uuid_vec_t ids;
- std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids));
- if (!is_only_items_selected(ids) && !is_only_cats_selected(ids))
- {
- disabled_items.push_back(std::string("New folder from selected"));
- }
- }
-
- if (obj->getIsLinkType())
- {
- items.push_back(std::string("Find Original"));
- if (isLinkedObjectMissing())
- {
- disabled_items.push_back(std::string("Find Original"));
- }
-
- items.push_back(std::string("Cut"));
- if (!isItemMovable() || !canMenuCut())
- {
- disabled_items.push_back(std::string("Cut"));
- }
- }
- else
- {
- if (LLAssetType::lookupCanLink(obj->getType()))
- {
- items.push_back(std::string("Find Links"));
- }
-
- if (!isInboxFolder() && !single_folder_root)
- {
- items.push_back(std::string("Rename"));
- if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0))
- {
- disabled_items.push_back(std::string("Rename"));
- }
- }
-
- items.push_back(std::string("thumbnail"));
- if (isLibraryItem())
- {
- disabled_items.push_back(std::string("thumbnail"));
- }
-
- LLViewerInventoryItem *inv_item = gInventory.getItem(mUUID);
- if (show_asset_id)
- {
- items.push_back(std::string("Copy Asset UUID"));
-
- bool is_asset_knowable = false;
-
- if (inv_item)
- {
- is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(inv_item->getType());
- }
- if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308
- || (! ( isItemPermissive() || gAgent.isGodlike() ) )
- || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Copy Asset UUID"));
- }
- }
-
- if(!single_folder_root)
- {
- items.push_back(std::string("Cut"));
- if (!isItemMovable() || !canMenuCut())
- {
- disabled_items.push_back(std::string("Cut"));
- }
-
- if (canListOnMarketplace() && !isMarketplaceListingsFolder() && !isInboxFolder())
- {
- items.push_back(std::string("Marketplace Separator"));
-
- if (gMenuHolder->getChild<LLView>("MarketplaceListings")->getVisible())
- {
- items.push_back(std::string("Marketplace Copy"));
- items.push_back(std::string("Marketplace Move"));
- if (!canListOnMarketplaceNow())
- {
- disabled_items.push_back(std::string("Marketplace Copy"));
- disabled_items.push_back(std::string("Marketplace Move"));
- }
- }
- }
- }
- }
- }
-
- // Don't allow items to be pasted directly into the COF or the inbox
- if (!isCOFFolder() && !isInboxFolder())
- {
- items.push_back(std::string("Paste"));
- }
- if (!isClipboardPasteable() || ((flags & FIRST_SELECTED_ITEM) == 0))
- {
- disabled_items.push_back(std::string("Paste"));
- }
-
- static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
- if (inventory_linking)
- {
- items.push_back(std::string("Paste As Link"));
- if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Paste As Link"));
- }
- }
-
- if (obj->getType() != LLAssetType::AT_CATEGORY)
- {
- items.push_back(std::string("Paste Separator"));
- }
-
- if(!single_folder_root)
- {
- addDeleteContextMenuOptions(items, disabled_items);
- }
-
- if (!isPanelActive("All Items") && !isPanelActive("comb_single_folder_inv"))
- {
- items.push_back(std::string("Show in Main Panel"));
- }
-}
-
-void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLInvFVBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
-
- addOpenRightClickMenuOption(items);
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-bool get_selection_item_uuids(LLFolderView::selected_items_t& selected_items, uuid_vec_t& ids)
-{
- uuid_vec_t results;
- S32 non_item = 0;
- for(LLFolderView::selected_items_t::iterator it = selected_items.begin(); it != selected_items.end(); ++it)
- {
- LLItemBridge *view_model = dynamic_cast<LLItemBridge *>((*it)->getViewModelItem());
-
- if(view_model && view_model->getUUID().notNull())
- {
- results.push_back(view_model->getUUID());
- }
- else
- {
- non_item++;
- }
- }
- if (non_item == 0)
- {
- ids = results;
- return true;
- }
- return false;
-}
-
-void LLInvFVBridge::addTrashContextMenuOptions(menuentry_vec_t &items,
- menuentry_vec_t &disabled_items)
-{
- const LLInventoryObject *obj = getInventoryObject();
- if (obj && obj->getIsLinkType())
- {
- items.push_back(std::string("Find Original"));
- if (isLinkedObjectMissing())
- {
- disabled_items.push_back(std::string("Find Original"));
- }
- }
- items.push_back(std::string("Purge Item"));
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Purge Item"));
- }
- items.push_back(std::string("Restore Item"));
-}
-
-void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items,
- menuentry_vec_t &disabled_items)
-{
-
- const LLInventoryObject *obj = getInventoryObject();
-
- // Don't allow delete as a direct option from COF folder.
- if (obj && obj->getIsLinkType() && isCOFFolder() && get_is_item_worn(mUUID))
- {
- return;
- }
-
- items.push_back(std::string("Delete"));
-
- if (isPanelActive("Favorite Items") || !canMenuDelete())
- {
- disabled_items.push_back(std::string("Delete"));
- }
-}
-
-void LLInvFVBridge::addOpenRightClickMenuOption(menuentry_vec_t &items)
-{
- const LLInventoryObject *obj = getInventoryObject();
- const bool is_link = (obj && obj->getIsLinkType());
-
- if (is_link)
- items.push_back(std::string("Open Original"));
- else
- items.push_back(std::string("Open"));
-}
-
-void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags,
- menuentry_vec_t &items,
- menuentry_vec_t &disabled_items)
-{
- S32 depth = depth_nesting_in_marketplace(mUUID);
- if (depth == 1)
- {
- // Options available at the Listing Folder level
- items.push_back(std::string("Marketplace Create Listing"));
- items.push_back(std::string("Marketplace Associate Listing"));
- items.push_back(std::string("Marketplace Check Listing"));
- items.push_back(std::string("Marketplace List"));
- items.push_back(std::string("Marketplace Unlist"));
- if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0))
- {
- // During SLM update, disable all marketplace related options
- // Also disable all if multiple selected items
- disabled_items.push_back(std::string("Marketplace Create Listing"));
- disabled_items.push_back(std::string("Marketplace Associate Listing"));
- disabled_items.push_back(std::string("Marketplace Check Listing"));
- disabled_items.push_back(std::string("Marketplace List"));
- disabled_items.push_back(std::string("Marketplace Unlist"));
- }
- else
- {
- if (gSavedSettings.getBOOL("MarketplaceListingsLogging"))
- {
- items.push_back(std::string("Marketplace Get Listing"));
- }
- if (LLMarketplaceData::instance().isListed(mUUID))
- {
- disabled_items.push_back(std::string("Marketplace Create Listing"));
- disabled_items.push_back(std::string("Marketplace Associate Listing"));
- if (LLMarketplaceData::instance().getVersionFolder(mUUID).isNull())
- {
- disabled_items.push_back(std::string("Marketplace List"));
- disabled_items.push_back(std::string("Marketplace Unlist"));
- }
- else
- {
- if (LLMarketplaceData::instance().getActivationState(mUUID))
- {
- disabled_items.push_back(std::string("Marketplace List"));
- }
- else
- {
- disabled_items.push_back(std::string("Marketplace Unlist"));
- }
- }
- }
- else
- {
- disabled_items.push_back(std::string("Marketplace List"));
- disabled_items.push_back(std::string("Marketplace Unlist"));
- if (gSavedSettings.getBOOL("MarketplaceListingsLogging"))
- {
- disabled_items.push_back(std::string("Marketplace Get Listing"));
- }
- }
- }
- }
- if (depth == 2)
- {
- // Options available at the Version Folder levels and only for folders
- LLInventoryCategory* cat = gInventory.getCategory(mUUID);
- if (cat && LLMarketplaceData::instance().isListed(cat->getParentUUID()))
- {
- items.push_back(std::string("Marketplace Activate"));
- items.push_back(std::string("Marketplace Deactivate"));
- if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0))
- {
- // During SLM update, disable all marketplace related options
- // Also disable all if multiple selected items
- disabled_items.push_back(std::string("Marketplace Activate"));
- disabled_items.push_back(std::string("Marketplace Deactivate"));
- }
- else
- {
- if (LLMarketplaceData::instance().isVersionFolder(mUUID))
- {
- disabled_items.push_back(std::string("Marketplace Activate"));
- if (LLMarketplaceData::instance().getActivationState(mUUID))
- {
- disabled_items.push_back(std::string("Marketplace Deactivate"));
- }
- }
- else
- {
- disabled_items.push_back(std::string("Marketplace Deactivate"));
- }
- }
- }
- }
-
- items.push_back(std::string("Marketplace Edit Listing"));
- LLUUID listing_folder_id = nested_parent_id(mUUID,depth);
- LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(listing_folder_id);
-
- if (depth >= 2)
- {
- // Prevent creation of new folders if the max count has been reached on this version folder (active or not)
- LLUUID local_version_folder_id = nested_parent_id(mUUID,depth-1);
- LLInventoryModel::cat_array_t categories;
- LLInventoryModel::item_array_t items;
- gInventory.collectDescendents(local_version_folder_id, categories, items, false);
- static LLCachedControl<U32> max_depth(gSavedSettings, "InventoryOutboxMaxFolderDepth", 4);
- static LLCachedControl<U32> max_count(gSavedSettings, "InventoryOutboxMaxFolderCount", 20);
- if (categories.size() >= max_count
- || depth > (max_depth + 1))
- {
- disabled_items.push_back(std::string("New Folder"));
- }
- }
-
- // Options available at all levels on items and categories
- if (!LLMarketplaceData::instance().isListed(listing_folder_id) || version_folder_id.isNull())
- {
- disabled_items.push_back(std::string("Marketplace Edit Listing"));
- }
-
- // Separator
- items.push_back(std::string("Marketplace Listings Separator"));
-}
-
-void LLInvFVBridge::addLinkReplaceMenuOption(menuentry_vec_t& items, menuentry_vec_t& disabled_items)
-{
- const LLInventoryObject* obj = getInventoryObject();
-
- if (isAgentInventory() && obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getType() != LLAssetType::AT_LINK_FOLDER)
- {
- items.push_back(std::string("Replace Links"));
-
- if (mRoot->getSelectedCount() != 1)
- {
- disabled_items.push_back(std::string("Replace Links"));
- }
- }
-}
-
-bool LLInvFVBridge::canMenuDelete()
-{
- return isItemRemovable(false);
-}
-
-bool LLInvFVBridge::canMenuCut()
-{
- return isItemRemovable(true);
-}
-
-// *TODO: remove this
-bool LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const
-{
- bool rv = false;
-
- const LLInventoryObject* obj = getInventoryObject();
-
- if(obj)
- {
- *type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType());
- if(*type == DAD_NONE)
- {
- return false;
- }
-
- *id = obj->getUUID();
- //object_ids.push_back(obj->getUUID());
-
- if (*type == DAD_CATEGORY)
- {
- LLInventoryModelBackgroundFetch::instance().start(obj->getUUID());
- }
-
- rv = true;
- }
-
- return rv;
-}
-
-LLInventoryObject* LLInvFVBridge::getInventoryObject() const
-{
- LLInventoryObject* obj = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- obj = (LLInventoryObject*)model->getObject(mUUID);
- }
- return obj;
-}
-
-LLInventoryModel* LLInvFVBridge::getInventoryModel() const
-{
- LLInventoryPanel* panel = mInventoryPanel.get();
- return panel ? panel->getModel() : NULL;
-}
-
-LLInventoryFilter* LLInvFVBridge::getInventoryFilter() const
-{
- LLInventoryPanel* panel = mInventoryPanel.get();
- return panel ? &(panel->getFilter()) : NULL;
-}
-
-bool LLInvFVBridge::isItemInTrash() const
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- return model->isObjectDescendentOf(mUUID, trash_id);
-}
-
-bool LLInvFVBridge::isLinkedObjectInTrash() const
-{
- if (isItemInTrash()) return true;
-
- const LLInventoryObject *obj = getInventoryObject();
- if (obj && obj->getIsLinkType())
- {
- LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id);
- }
- return false;
-}
-
-bool LLInvFVBridge::isItemInOutfits() const
-{
- const LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
-
- const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
-
- return isCOFFolder() || (my_outfits_cat == mUUID) || model->isObjectDescendentOf(mUUID, my_outfits_cat);
-}
-
-bool LLInvFVBridge::isLinkedObjectMissing() const
-{
- const LLInventoryObject *obj = getInventoryObject();
- if (!obj)
- {
- return true;
- }
- if (obj->getIsLinkType() && LLAssetType::lookupIsLinkType(obj->getType()))
- {
- return true;
- }
- return false;
-}
-
-bool LLInvFVBridge::isAgentInventory() const
-{
- const LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
- if(gInventory.getRootFolderID() == mUUID) return true;
- return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
-}
-
-bool LLInvFVBridge::isCOFFolder() const
-{
- return LLAppearanceMgr::instance().getIsInCOF(mUUID);
-}
-
-// *TODO : Suppress isInboxFolder() once Merchant Outbox is fully deprecated
-bool LLInvFVBridge::isInboxFolder() const
-{
- const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);
-
- if (inbox_id.isNull())
- {
- return false;
- }
-
- return gInventory.isObjectDescendentOf(mUUID, inbox_id);
-}
-
-bool LLInvFVBridge::isMarketplaceListingsFolder() const
-{
- const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
-
- if (folder_id.isNull())
- {
- return false;
- }
-
- return gInventory.isObjectDescendentOf(mUUID, folder_id);
-}
-
-bool LLInvFVBridge::isItemPermissive() const
-{
- return false;
-}
-
-// static
-void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
- LLViewerInventoryItem* item,
- const LLUUID& new_parent_id,
- bool restamp)
-{
- model->changeItemParent(item, new_parent_id, restamp);
-}
-
-// static
-void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
- LLViewerInventoryCategory* cat,
- const LLUUID& new_parent_id,
- bool restamp)
-{
- model->changeCategoryParent(cat, new_parent_id, restamp);
-}
-
-LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
- LLAssetType::EType actual_asset_type,
- LLInventoryType::EType inv_type,
- LLInventoryPanel* inventory,
- LLFolderViewModelInventory* view_model,
- LLFolderView* root,
- const LLUUID& uuid,
- U32 flags)
-{
- LLInvFVBridge* new_listener = NULL;
- switch(asset_type)
- {
- case LLAssetType::AT_TEXTURE:
- if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLTextureBridge(inventory, root, uuid, inv_type);
- break;
-
- case LLAssetType::AT_SOUND:
- if(!(inv_type == LLInventoryType::IT_SOUND))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLSoundBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_LANDMARK:
- if(!(inv_type == LLInventoryType::IT_LANDMARK))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLLandmarkBridge(inventory, root, uuid, flags);
- break;
-
- case LLAssetType::AT_CALLINGCARD:
- if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLCallingCardBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_SCRIPT:
- if(!(inv_type == LLInventoryType::IT_LSL))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLItemBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_OBJECT:
- if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLObjectBridge(inventory, root, uuid, inv_type, flags);
- break;
-
- case LLAssetType::AT_NOTECARD:
- if(!(inv_type == LLInventoryType::IT_NOTECARD))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLNotecardBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_ANIMATION:
- if(!(inv_type == LLInventoryType::IT_ANIMATION))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLAnimationBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_GESTURE:
- if(!(inv_type == LLInventoryType::IT_GESTURE))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLGestureBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_LSL_TEXT:
- if(!(inv_type == LLInventoryType::IT_LSL))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLLSLTextBridge(inventory, root, uuid);
- break;
-
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_BODYPART:
- if(!(inv_type == LLInventoryType::IT_WEARABLE))
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, LLWearableType::inventoryFlagsToWearableType(flags));
- break;
- case LLAssetType::AT_CATEGORY:
- if (actual_asset_type == LLAssetType::AT_LINK_FOLDER)
- {
- // Create a link folder handler instead
- new_listener = new LLLinkFolderBridge(inventory, root, uuid);
- }
- else if (actual_asset_type == LLAssetType::AT_MARKETPLACE_FOLDER)
- {
- // Create a marketplace folder handler
- new_listener = new LLMarketplaceFolderBridge(inventory, root, uuid);
- }
- else
- {
- new_listener = new LLFolderBridge(inventory, root, uuid);
- }
- break;
- case LLAssetType::AT_LINK:
- case LLAssetType::AT_LINK_FOLDER:
- // Only should happen for broken links.
- new_listener = new LLLinkItemBridge(inventory, root, uuid);
- break;
- case LLAssetType::AT_UNKNOWN:
- new_listener = new LLUnknownItemBridge(inventory, root, uuid);
- break;
- case LLAssetType::AT_IMAGE_TGA:
- case LLAssetType::AT_IMAGE_JPEG:
- //LL_WARNS() << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << LL_ENDL;
- break;
-
- case LLAssetType::AT_SETTINGS:
- if (inv_type != LLInventoryType::IT_SETTINGS)
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLSettingsBridge(inventory, root, uuid, LLSettingsType::fromInventoryFlags(flags));
- break;
-
- case LLAssetType::AT_MATERIAL:
- if (inv_type != LLInventoryType::IT_MATERIAL)
- {
- LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;
- }
- new_listener = new LLMaterialBridge(inventory, root, uuid);
- break;
-
- default:
- LL_INFOS_ONCE() << "Unhandled asset type (llassetstorage.h): "
- << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << LL_ENDL;
- break;
- }
-
- if (new_listener)
- {
- new_listener->mInvType = inv_type;
- }
-
- return new_listener;
-}
-
-void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
-{
- LLInventoryObject* obj = model->getObject(uuid);
- if (obj)
- {
- remove_inventory_object(uuid, NULL);
- }
-}
-
-void LLInvFVBridge::removeObject(LLInventoryModel *model, const LLUUID &uuid)
-{
- // Keep track of the parent
- LLInventoryItem* itemp = model->getItem(uuid);
- LLUUID parent_id = (itemp ? itemp->getParentUUID() : LLUUID::null);
- // Remove the object
- model->removeObject(uuid);
- // Get the parent updated
- if (parent_id.notNull())
- {
- LLViewerInventoryCategory* parent_cat = model->getCategory(parent_id);
- model->updateCategory(parent_cat);
- model->notifyObservers();
- }
-}
-
-bool LLInvFVBridge::canShare() const
-{
- bool can_share = false;
-
- if (isAgentInventory())
- {
- const LLInventoryModel* model = getInventoryModel();
- if (model)
- {
- const LLViewerInventoryItem *item = model->getItem(mUUID);
- if (item)
- {
- if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item))
- {
- can_share = LLGiveInventory::isInventoryGiveAcceptable(item);
- }
- }
- else
- {
- // Categories can be given.
- can_share = (model->getCategory(mUUID) != NULL);
- }
-
- const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
- if ((mUUID == trash_id) || gInventory.isObjectDescendentOf(mUUID, trash_id))
- {
- can_share = false;
- }
- }
- }
-
- return can_share;
-}
-
-bool LLInvFVBridge::canListOnMarketplace() const
-{
- LLInventoryModel * model = getInventoryModel();
-
- LLViewerInventoryCategory * cat = model->getCategory(mUUID);
- if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
- {
- return false;
- }
-
- if (!isAgentInventory())
- {
- return false;
- }
-
- LLViewerInventoryItem * item = model->getItem(mUUID);
- if (item)
- {
- if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
- {
- return false;
- }
-
- if (LLAssetType::AT_CALLINGCARD == item->getType())
- {
- return false;
- }
- }
-
- return true;
-}
-
-bool LLInvFVBridge::canListOnMarketplaceNow() const
-{
- bool can_list = true;
-
- const LLInventoryObject* obj = getInventoryObject();
- can_list &= (obj != NULL);
-
- if (can_list)
- {
- const LLUUID& object_id = obj->getLinkedUUID();
- can_list = object_id.notNull();
-
- if (can_list)
- {
- LLFolderViewFolder * object_folderp = mInventoryPanel.get() ? mInventoryPanel.get()->getFolderByID(object_id) : NULL;
- if (object_folderp)
- {
- can_list = !static_cast<LLFolderBridge*>(object_folderp->getViewModelItem())->isLoading();
- }
- }
-
- if (can_list)
- {
- std::string error_msg;
- LLInventoryModel* model = getInventoryModel();
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- if (marketplacelistings_id.notNull())
- {
- LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id);
- LLInventoryCategory *cat = model->getCategory(mUUID);
- if (cat)
- {
- can_list = can_move_folder_to_marketplace(master_folder, master_folder, cat, error_msg);
- }
- else
- {
- LLInventoryItem *item = model->getItem(mUUID);
- can_list = (item ? can_move_item_to_marketplace(master_folder, master_folder, item, error_msg) : false);
- }
- }
- else
- {
- can_list = false;
- }
- }
- }
-
- return can_list;
-}
-
-LLToolDragAndDrop::ESource LLInvFVBridge::getDragSource() const
-{
- if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID()))
- {
- return LLToolDragAndDrop::SOURCE_AGENT;
- }
- else if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID()))
- {
- return LLToolDragAndDrop::SOURCE_LIBRARY;
- }
-
- return LLToolDragAndDrop::SOURCE_VIEWER;
-}
-
-
-
-// +=================================================+
-// | InventoryFVBridgeBuilder |
-// +=================================================+
-LLInvFVBridge* LLInventoryFolderViewModelBuilder::createBridge(LLAssetType::EType asset_type,
- LLAssetType::EType actual_asset_type,
- LLInventoryType::EType inv_type,
- LLInventoryPanel* inventory,
- LLFolderViewModelInventory* view_model,
- LLFolderView* root,
- const LLUUID& uuid,
- U32 flags /* = 0x00 */) const
-{
- return LLInvFVBridge::createBridge(asset_type,
- actual_asset_type,
- inv_type,
- inventory,
- view_model,
- root,
- uuid,
- flags);
-}
-
-// +=================================================+
-// | LLItemBridge |
-// +=================================================+
-
-void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("goto" == action)
- {
- gotoItem();
- }
-
- if ("open" == action || "open_original" == action)
- {
- openItem();
- return;
- }
- else if ("properties" == action)
- {
- showProperties();
- return;
- }
- else if ("purge" == action)
- {
- purgeItem(model, mUUID);
- return;
- }
- else if ("restoreToWorld" == action)
- {
- restoreToWorld();
- return;
- }
- else if ("restore" == action)
- {
- restoreItem();
- return;
- }
- else if ("thumbnail" == action)
- {
- LLSD data(mUUID);
- LLFloaterReg::showInstance("change_item_thumbnail", data);
- return;
- }
- else if ("copy_uuid" == action)
- {
- // Single item only
- LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
- if(!item) return;
- LLUUID asset_id = item->getProtectedAssetUUID();
- std::string buffer;
- asset_id.toString(buffer);
-
- gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer));
- return;
- }
- else if ("show_in_main_panel" == action)
- {
- LLInventoryPanel::openInventoryPanelAndSetSelection(true, mUUID, true);
- return;
- }
- else if ("cut" == action)
- {
- cutToClipboard();
- return;
- }
- else if ("copy" == action)
- {
- copyToClipboard();
- return;
- }
- else if ("paste" == action)
- {
- LLInventoryItem* itemp = model->getItem(mUUID);
- if (!itemp) return;
-
- LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID());
- if (!folder_view_itemp) return;
-
- folder_view_itemp->getViewModelItem()->pasteFromClipboard();
- return;
- }
- else if ("paste_link" == action)
- {
- // Single item only
- LLInventoryItem* itemp = model->getItem(mUUID);
- if (!itemp) return;
-
- LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID());
- if (!folder_view_itemp) return;
-
- folder_view_itemp->getViewModelItem()->pasteLinkFromClipboard();
- return;
- }
- else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action))
- {
- LLInventoryItem* itemp = model->getItem(mUUID);
- if (!itemp) return;
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- // Note: For a single item, if it's not a copy, then it's a move
- move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action));
- }
- else if ("copy_slurl" == action)
- {
- LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
- if(item)
- {
- LLUUID asset_id = item->getAssetUUID();
- LLLandmark* landmark = gLandmarkList.getAsset(asset_id);
- if (landmark)
- {
- LLVector3d global_pos;
- landmark->getGlobalPos(global_pos);
- LLLandmarkActions::getSLURLfromPosGlobal(global_pos, ©_slurl_to_clipboard_callback_inv, true);
- }
- }
- }
- else if ("show_on_map" == action)
- {
- doActionOnCurSelectedLandmark(boost::bind(&LLItemBridge::doShowOnMap, this, _1));
- }
- else if ("marketplace_edit_listing" == action)
- {
- std::string url = LLMarketplaceData::instance().getListingURL(mUUID);
- LLUrlAction::openURL(url);
- }
-}
-
-void LLItemBridge::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb)
-{
- LLViewerInventoryItem* cur_item = getItem();
- if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK)
- {
- LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb);
- if (landmark)
- {
- cb(landmark);
- }
- }
-}
-
-void LLItemBridge::doShowOnMap(LLLandmark* landmark)
-{
- LLVector3d landmark_global_pos;
- // landmark has already been tested for NULL by calling routine
- if (landmark->getGlobalPos(landmark_global_pos))
- {
- LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
- if (!landmark_global_pos.isExactlyZero() && worldmap_instance)
- {
- worldmap_instance->trackLocation(landmark_global_pos);
- LLFloaterReg::showInstance("world_map", "center");
- }
- }
-}
-
-void copy_slurl_to_clipboard_callback_inv(const std::string& slurl)
-{
- gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
- LLSD args;
- args["SLURL"] = slurl;
- LLNotificationsUtil::add("CopySLURL", args);
-}
-
-void LLItemBridge::selectItem()
-{
- LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
- if(item && !item->isFinished())
- {
- //item->fetchFromServer();
- LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false);
- }
-}
-
-void LLItemBridge::restoreItem()
-{
- LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
- if(item)
- {
- LLInventoryModel* model = getInventoryModel();
- bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT);
-
- const LLUUID new_parent = model->findCategoryUUIDForType(is_snapshot? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType()));
- // do not restamp on restore.
- LLInvFVBridge::changeItemParent(model, item, new_parent, false);
- }
-}
-
-void LLItemBridge::restoreToWorld()
-{
- //Similar functionality to the drag and drop rez logic
- bool remove_from_inventory = false;
-
- LLViewerInventoryItem* itemp = static_cast<LLViewerInventoryItem*>(getItem());
- if (itemp)
- {
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("RezRestoreToWorld");
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-
- msg->nextBlockFast(_PREHASH_InventoryData);
- itemp->packMessage(msg);
- msg->sendReliable(gAgent.getRegionHost());
-
- //remove local inventory copy, sim will deal with permissions and removing the item
- //from the actual inventory if its a no-copy etc
- if(!itemp->getPermissions().allowCopyBy(gAgent.getID()))
- {
- remove_from_inventory = true;
- }
-
- // Check if it's in the trash. (again similar to the normal rez logic)
- const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
- if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id))
- {
- remove_from_inventory = true;
- }
- }
-
- if(remove_from_inventory)
- {
- gInventory.deleteObject(itemp->getUUID());
- gInventory.notifyObservers();
- }
-}
-
-void LLItemBridge::gotoItem()
-{
- LLInventoryObject *obj = getInventoryObject();
- if (obj && obj->getIsLinkType())
- {
- show_item_original(obj->getUUID());
- }
-}
-
-LLUIImagePtr LLItemBridge::getIcon() const
-{
- LLInventoryObject *obj = getInventoryObject();
- if (obj)
- {
- return LLInventoryIcon::getIcon(obj->getType(),
- LLInventoryType::IT_NONE,
- mIsLink);
- }
-
- return LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT);
-}
-
-LLUIImagePtr LLItemBridge::getIconOverlay() const
-{
- if (getItem() && getItem()->getIsLinkType())
- {
- return LLUI::getUIImage("Inv_Link");
- }
- return NULL;
-}
-
-PermissionMask LLItemBridge::getPermissionMask() const
-{
- LLViewerInventoryItem* item = getItem();
- PermissionMask perm_mask = 0;
- if (item) perm_mask = item->getPermissionMask();
- return perm_mask;
-}
-
-void LLItemBridge::buildDisplayName() const
-{
- if(getItem())
- {
- mDisplayName.assign(getItem()->getName());
- }
- else
- {
- mDisplayName.assign(LLStringUtil::null);
- }
-
- mSearchableName.assign(mDisplayName);
- mSearchableName.append(getLabelSuffix());
- LLStringUtil::toUpper(mSearchableName);
-
- //Name set, so trigger a sort
- LLInventorySort sorter = static_cast<LLFolderViewModelInventory&>(mRootViewModel).getSorter();
- if(mParent && !sorter.isByDate())
- {
- mParent->requestSort();
- }
-}
-
-LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const
-{
- U8 font = LLFontGL::NORMAL;
- const LLViewerInventoryItem* item = getItem();
-
- if (get_is_item_worn(mUUID))
- {
- // LL_INFOS() << "BOLD" << LL_ENDL;
- font |= LLFontGL::BOLD;
- }
- else if(item && item->getIsLinkType())
- {
- font |= LLFontGL::ITALIC;
- }
-
- return (LLFontGL::StyleFlags)font;
-}
-
-std::string LLItemBridge::getLabelSuffix() const
-{
- // String table is loaded before login screen and inventory items are
- // loaded after login, so LLTrans should be ready.
- static std::string NO_COPY = LLTrans::getString("no_copy_lbl");
- static std::string NO_MOD = LLTrans::getString("no_modify_lbl");
- static std::string NO_XFER = LLTrans::getString("no_transfer_lbl");
- static std::string LINK = LLTrans::getString("link");
- static std::string BROKEN_LINK = LLTrans::getString("broken_link");
- std::string suffix;
- LLInventoryItem* item = getItem();
- if(item)
- {
- // Any type can have the link suffix...
- bool broken_link = LLAssetType::lookupIsLinkType(item->getType());
- if (broken_link) return BROKEN_LINK;
-
- bool link = item->getIsLinkType();
- if (link) return LINK;
-
- // ...but it's a bit confusing to put nocopy/nomod/etc suffixes on calling cards.
- if(LLAssetType::AT_CALLINGCARD != item->getType()
- && item->getPermissions().getOwner() == gAgent.getID())
- {
- bool copy = item->getPermissions().allowCopyBy(gAgent.getID());
- if (!copy)
- {
- suffix += " ";
- suffix += NO_COPY;
- }
- bool mod = item->getPermissions().allowModifyBy(gAgent.getID());
- if (!mod)
- {
- suffix += suffix.empty() ? " " : ",";
- suffix += NO_MOD;
- }
- bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
- gAgent.getID());
- if (!xfer)
- {
- suffix += suffix.empty() ? " " : ",";
- suffix += NO_XFER;
- }
- }
- }
- return suffix;
-}
-
-time_t LLItemBridge::getCreationDate() const
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- return item->getCreationDate();
- }
- return 0;
-}
-
-
-bool LLItemBridge::isItemRenameable() const
-{
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- // (For now) Don't allow calling card rename since that may confuse users as to
- // what the calling card points to.
- if (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
- {
- return false;
- }
-
- if (!item->isFinished()) // EXT-8662
- {
- return false;
- }
-
- if (isInboxFolder())
- {
- return false;
- }
-
- return (item->getPermissions().allowModifyBy(gAgent.getID()));
- }
- return false;
-}
-
-bool LLItemBridge::renameItem(const std::string& new_name)
-{
- if(!isItemRenameable())
- return false;
- LLPreview::dirty(mUUID);
- LLInventoryModel* model = getInventoryModel();
- if(!model)
- return false;
- LLViewerInventoryItem* item = getItem();
- if(item && (item->getName() != new_name))
- {
- LLSD updates;
- updates["name"] = new_name;
- update_inventory_item(item->getUUID(),updates, NULL);
- }
- // return false because we either notified observers (& therefore
- // rebuilt) or we didn't update.
- return false;
-}
-
-bool LLItemBridge::removeItem()
-{
- if(!isItemRemovable())
- {
- return false;
- }
-
- // move it to the trash
- LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
- const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- LLViewerInventoryItem* item = getItem();
- if (!item) return false;
- if (item->getType() != LLAssetType::AT_LSL_TEXT)
- {
- LLPreview::hide(mUUID, true);
- }
- // Already in trash
- if (model->isObjectDescendentOf(mUUID, trash_id)) return false;
-
- LLNotification::Params params("ConfirmItemDeleteHasLinks");
- params.functor.function(boost::bind(&LLItemBridge::confirmRemoveItem, this, _1, _2));
-
- // Check if this item has any links. If generic inventory linking is enabled,
- // we can't do this check because we may have items in a folder somewhere that is
- // not yet in memory, so we don't want false negatives. (If disabled, then we
- // know we only have links in the Outfits folder which we explicitly fetch.)
- static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
- if (!inventory_linking)
- {
- if (!item->getIsLinkType())
- {
- LLInventoryModel::item_array_t item_array = gInventory.collectLinksTo(mUUID);
- const U32 num_links = item_array.size();
- if (num_links > 0)
- {
- // Warn if the user is will break any links when deleting this item.
- LLNotifications::instance().add(params);
- return false;
- }
- }
- }
-
- LLNotifications::instance().forceResponse(params, 0);
- model->checkTrashOverflow();
- return true;
-}
-
-bool LLItemBridge::confirmRemoveItem(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0) return false;
-
- LLInventoryModel* model = getInventoryModel();
- if (!model) return false;
-
- LLViewerInventoryItem* item = getItem();
- if (!item) return false;
-
- const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- // if item is not already in trash
- if(item && !model->isObjectDescendentOf(mUUID, trash_id))
- {
- // move to trash, and restamp
- LLInvFVBridge::changeItemParent(model, item, trash_id, true);
- // delete was successful
- return true;
- }
- return false;
-}
-
-bool LLItemBridge::isItemCopyable(bool can_copy_as_link) const
-{
- LLViewerInventoryItem* item = getItem();
- if (!item)
- {
- return false;
- }
- // Can't copy worn objects.
- // Worn objects are tied to their inworld conterparts
- // Copy of modified worn object will return object with obsolete asset and inventory
- if (get_is_item_worn(mUUID))
- {
- return false;
- }
-
- static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
- return (can_copy_as_link && inventory_linking)
- || (mIsLink && inventory_linking)
- || item->getPermissions().allowCopyBy(gAgent.getID());
-}
-
-LLViewerInventoryItem* LLItemBridge::getItem() const
-{
- LLViewerInventoryItem* item = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- item = (LLViewerInventoryItem*)model->getItem(mUUID);
- }
- return item;
-}
-
-const LLUUID& LLItemBridge::getThumbnailUUID() const
-{
- LLViewerInventoryItem* item = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- item = (LLViewerInventoryItem*)model->getItem(mUUID);
- }
- if (item)
- {
- return item->getThumbnailUUID();
- }
- return LLUUID::null;
-}
-
-bool LLItemBridge::isItemPermissive() const
-{
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- return item->getIsFullPerm();
- }
- return false;
-}
-
-// +=================================================+
-// | LLFolderBridge |
-// +=================================================+
-
-LLHandle<LLFolderBridge> LLFolderBridge::sSelf;
-
-// Can be moved to another folder
-bool LLFolderBridge::isItemMovable() const
-{
- LLInventoryObject* obj = getInventoryObject();
- if(obj)
- {
- // If it's a protected type folder, we can't move it
- if (LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType()))
- return false;
- return true;
- }
- return false;
-}
-
-void LLFolderBridge::selectItem()
-{
- LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID());
- if (cat)
- {
- cat->fetch();
- }
-}
-
-void LLFolderBridge::buildDisplayName() const
-{
- LLFolderType::EType preferred_type = getPreferredType();
-
- // *TODO: to be removed when database supports multi language. This is a
- // temporary attempt to display the inventory folder in the user locale.
- // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID
- // it uses the same way to find localized string
-
- // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder)
- // Translation of Accessories folder in Library inventory folder
- bool accessories = false;
- if(getName() == "Accessories")
- {
- //To ensure that Accessories folder is in Library we have to check its parent folder.
- //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model
- LLInventoryCategory* cat = gInventory.getCategory(getUUID());
- if(cat)
- {
- const LLUUID& parent_folder_id = cat->getParentUUID();
- accessories = (parent_folder_id == gInventory.getLibraryRootFolderID());
- }
- }
-
- //"Accessories" inventory category has folder type FT_NONE. So, this folder
- //can not be detected as protected with LLFolderType::lookupIsProtectedType
- mDisplayName.assign(getName());
- if (accessories || LLFolderType::lookupIsProtectedType(preferred_type))
- {
- LLTrans::findString(mDisplayName, std::string("InvFolder ") + getName(), LLSD());
- }
-
- mSearchableName.assign(mDisplayName);
- mSearchableName.append(getLabelSuffix());
- LLStringUtil::toUpper(mSearchableName);
-
- //Name set, so trigger a sort
- LLInventorySort sorter = static_cast<LLFolderViewModelInventory&>(mRootViewModel).getSorter();
- if(mParent && sorter.isFoldersByName())
- {
- mParent->requestSort();
- }
-}
-
-std::string LLFolderBridge::getLabelSuffix() const
-{
- static LLCachedControl<bool> xui_debug(gSavedSettings, "DebugShowXUINames", 0);
-
- if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= FOLDER_LOADING_MESSAGE_DELAY)
- {
- return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str());
- }
- std::string suffix = "";
- if (xui_debug)
- {
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- gInventory.getDirectDescendentsOf(getUUID(), cats, items);
-
- LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID());
- if (cat)
- {
- LLStringUtil::format_map_t args;
- args["[FOLDER_COUNT]"] = llformat("%d", cats->size());
- args["[ITEMS_COUNT]"] = llformat("%d", items->size());
- args["[VERSION]"] = llformat("%d", cat->getVersion());
- args["[VIEWER_DESCENDANT_COUNT]"] = llformat("%d", cats->size() + items->size());
- args["[SERVER_DESCENDANT_COUNT]"] = llformat("%d", cat->getDescendentCount());
- suffix = " " + LLTrans::getString("InventoryFolderDebug", args);
- }
- }
- else if(mShowDescendantsCount)
- {
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- gInventory.collectDescendents(getUUID(), cat_array, item_array, true);
- S32 count = item_array.size();
- if(count > 0)
- {
- std::ostringstream oss;
- oss << count;
- LLStringUtil::format_map_t args;
- args["[ITEMS_COUNT]"] = oss.str();
- suffix = " " + LLTrans::getString("InventoryItemsCount", args);
- }
- }
-
- return LLInvFVBridge::getLabelSuffix() + suffix;
-}
-
-LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const
-{
- return LLFontGL::NORMAL;
-}
-
-const LLUUID& LLFolderBridge::getThumbnailUUID() const
-{
- LLViewerInventoryCategory* cat = getCategory();
- if (cat)
- {
- return cat->getThumbnailUUID();
- }
- return LLUUID::null;
-}
-
-void LLFolderBridge::update()
-{
- // we know we have children but haven't fetched them (doesn't obey filter)
- bool loading = !isUpToDate() && hasChildren() && mFolderViewItem->isOpen();
-
- if (loading != mIsLoading)
- {
- if ( loading )
- {
- // Measure how long we've been in the loading state
- mTimeSinceRequestStart.reset();
- }
- mIsLoading = loading;
-
- mFolderViewItem->refresh();
- }
-}
-
-// Can be destroyed (or moved to trash)
-bool LLFolderBridge::isItemRemovable(bool check_worn) const
-{
- if (!get_is_category_and_children_removable(getInventoryModel(), mUUID, check_worn))
- {
- return false;
- }
-
- if (isMarketplaceListingsFolder()
- && (!LLMarketplaceData::instance().isSLMDataFetched() || LLMarketplaceData::instance().getActivationState(mUUID)))
- {
- return false;
- }
-
- return true;
-}
-
-bool LLFolderBridge::isUpToDate() const
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
- LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- if( !category )
- {
- return false;
- }
-
- return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
-}
-
-bool LLFolderBridge::isItemCopyable(bool can_copy_as_link) const
-{
- if (can_copy_as_link && !LLFolderType::lookupIsProtectedType(getPreferredType()))
- {
- // Can copy and paste unprotected folders as links
- return true;
- }
-
- // Folders are copyable if items in them are, recursively, copyable.
-
- // Get the content of the folder
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(mUUID,cat_array,item_array);
-
- // Check the items
- LLInventoryModel::item_array_t item_array_copy = *item_array;
- for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
- {
- LLInventoryItem* item = *iter;
- LLItemBridge item_br(mInventoryPanel.get(), mRoot, item->getUUID());
- if (!item_br.isItemCopyable(false))
- {
- return false;
- }
- }
-
- // Check the folders
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
- {
- LLViewerInventoryCategory* category = *iter;
- LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, category->getUUID());
- if (!cat_br.isItemCopyable(false))
- {
- return false;
- }
- }
-
- return true;
-}
-
-bool LLFolderBridge::isClipboardPasteable() const
-{
- if ( ! LLInvFVBridge::isClipboardPasteable() )
- return false;
-
- // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
- if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) )
- {
- LLInventoryModel* model = getInventoryModel();
- if ( !model )
- {
- return false;
- }
-
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
- const LLViewerInventoryCategory *current_cat = getCategory();
-
- // Search for the direct descendent of current Friends subfolder among all pasted items,
- // and return false if is found.
- for(S32 i = objects.size() - 1; i >= 0; --i)
- {
- const LLUUID &obj_id = objects.at(i);
- if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
- {
- return false;
- }
- }
-
- }
- return true;
-}
-
-bool LLFolderBridge::isClipboardPasteableAsLink() const
-{
- // Check normal paste-as-link permissions
- if (!LLInvFVBridge::isClipboardPasteableAsLink())
- {
- return false;
- }
-
- const LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return false;
- }
-
- const LLViewerInventoryCategory *current_cat = getCategory();
- if (current_cat)
- {
- const bool is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat );
- const LLUUID ¤t_cat_id = current_cat->getUUID();
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
- S32 count = objects.size();
- for(S32 i = 0; i < count; i++)
- {
- const LLUUID &obj_id = objects.at(i);
- const LLInventoryCategory *cat = model->getCategory(obj_id);
- if (cat)
- {
- const LLUUID &cat_id = cat->getUUID();
- // Don't allow recursive pasting
- if ((cat_id == current_cat_id) ||
- model->isObjectDescendentOf(current_cat_id, cat_id))
- {
- return false;
- }
- }
- // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
- if ( is_in_friend_folder )
- {
- // If object is direct descendent of current Friends subfolder than return false.
- // Note: We can't use 'const LLInventoryCategory *cat', because it may be null
- // in case type of obj_id is LLInventoryItem.
- if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
- {
- return false;
- }
- }
- }
- }
- return true;
-
-}
-
-
-bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
- bool drop,
- std::string& tooltip_msg,
- bool is_link,
- bool user_confirm,
- LLPointer<LLInventoryCallback> cb)
-{
-
- LLInventoryModel* model = getInventoryModel();
-
- if (!inv_cat) return false; // shouldn't happen, but in case item is incorrectly parented in which case inv_cat will be NULL
- if (!model) return false;
- if (!isAgentAvatarValid()) return false;
- if (!isAgentInventory()) return false; // cannot drag categories into library
-
- LLInventoryPanel* destination_panel = mInventoryPanel.get();
- if (!destination_panel) return false;
-
- LLInventoryFilter* filter = getInventoryFilter();
- if (!filter) return false;
-
- const LLUUID &cat_id = inv_cat->getUUID();
- const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const LLUUID from_folder_uuid = inv_cat->getParentUUID();
-
- const bool move_is_into_current_outfit = (mUUID == current_outfit_id);
- const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
- const bool move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id);
-
- // check to make sure source is agent inventory, and is represented there.
- LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
- const bool is_agent_inventory = (model->getCategory(cat_id) != NULL)
- && (LLToolDragAndDrop::SOURCE_AGENT == source);
-
- bool accept = false;
- U64 filter_types = filter->getFilterTypes();
- bool use_filter = filter_types && (filter_types&LLInventoryFilter::FILTERTYPE_DATE || (filter_types&LLInventoryFilter::FILTERTYPE_OBJECT)==0);
-
- if (is_agent_inventory)
- {
- const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
- const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
-
- const bool move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
- const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
- const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
- const bool move_is_into_current_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_CURRENT_OUTFIT);
- const bool move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id);
- const bool move_is_into_lost_and_found = model->isObjectDescendentOf(mUUID, lost_and_found_id);
-
- //--------------------------------------------------------------------------------
- // Determine if folder can be moved.
- //
-
- bool is_movable = true;
-
- if (is_movable && (marketplacelistings_id == cat_id))
- {
- is_movable = false;
- tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot");
- }
- if (is_movable && move_is_from_marketplacelistings && LLMarketplaceData::instance().getActivationState(cat_id))
- {
- // If the incoming folder is listed and active (and is therefore either the listing or the version folder),
- // then moving is *not* allowed
- is_movable = false;
- tooltip_msg = LLTrans::getString("TooltipOutboxDragActive");
- }
- if (is_movable && (mUUID == cat_id))
- {
- is_movable = false;
- tooltip_msg = LLTrans::getString("TooltipDragOntoSelf");
- }
- if (is_movable && (model->isObjectDescendentOf(mUUID, cat_id)))
- {
- is_movable = false;
- tooltip_msg = LLTrans::getString("TooltipDragOntoOwnChild");
- }
- if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()))
- {
- is_movable = false;
- // tooltip?
- }
-
- U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
- if (is_movable && move_is_into_outfit)
- {
- if (mUUID == my_outifts_id)
- {
- if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings)
- {
- tooltip_msg = LLTrans::getString("TooltipOutfitNotInInventory");
- is_movable = false;
- }
- else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear))
- {
- is_movable = true;
- }
- else
- {
- tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
- is_movable = false;
- }
- }
- else if(getCategory() && getCategory()->getPreferredType() == LLFolderType::FT_NONE)
- {
- is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT));
- }
- else
- {
- is_movable = false;
- }
- }
- if(is_movable && move_is_into_current_outfit && is_link)
- {
- is_movable = false;
- }
- if (is_movable && move_is_into_lost_and_found)
- {
- is_movable = false;
- }
- if (is_movable && (mUUID == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE)))
- {
- is_movable = false;
- // tooltip?
- }
- if (is_movable && (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK))
- {
- // One cannot move a folder into a stock folder
- is_movable = false;
- // tooltip?
- }
-
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- if (is_movable)
- {
- model->collectDescendents(cat_id, descendent_categories, descendent_items, false);
- for (S32 i=0; i < descendent_categories.size(); ++i)
- {
- LLInventoryCategory* category = descendent_categories[i];
- if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
- {
- // Can't move "special folders" (e.g. Textures Folder).
- is_movable = false;
- break;
- }
- }
- }
- if (is_movable
- && move_is_into_current_outfit
- && descendent_items.size() > max_items_to_wear)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- not_worn);
-
- if (items.size() > max_items_to_wear)
- {
- // Can't move 'large' folders into current outfit: MAINT-4086
- is_movable = false;
- LLStringUtil::format_map_t args;
- args["AMOUNT"] = llformat("%d", max_items_to_wear);
- tooltip_msg = LLTrans::getString("TooltipTooManyWearables",args);
- }
- }
- if (is_movable && move_is_into_trash)
- {
- for (S32 i=0; i < descendent_items.size(); ++i)
- {
- LLInventoryItem* item = descendent_items[i];
- if (get_is_item_worn(item->getUUID()))
- {
- is_movable = false;
- break; // It's generally movable, but not into the trash.
- }
- }
- }
- if (is_movable && move_is_into_landmarks)
- {
- for (S32 i=0; i < descendent_items.size(); ++i)
- {
- LLViewerInventoryItem* item = descendent_items[i];
-
- // Don't move anything except landmarks and categories into Landmarks folder.
- // We use getType() instead of getActua;Type() to allow links to landmarks and folders.
- if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType())
- {
- is_movable = false;
- break; // It's generally movable, but not into Landmarks.
- }
- }
- }
-
- if (is_movable && move_is_into_marketplacelistings)
- {
- const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID);
- LLViewerInventoryCategory * dest_folder = getCategory();
- S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount());
- is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size);
- }
-
- if (is_movable && !move_is_into_landmarks)
- {
- LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(false);
- is_movable = active_panel != NULL;
-
- // For a folder to pass the filter all its descendants are required to pass.
- // We make this exception to allow reordering folders within an inventory panel,
- // which has a filter applied, like Recent tab for example.
- // There may be folders which are displayed because some of their descendants pass
- // the filter, but other don't, and thus remain hidden. Without this check,
- // such folders would not be allowed to be moved within a panel.
- if (destination_panel == active_panel)
- {
- is_movable = true;
- }
- else
- {
- LLFolderView* active_folder_view = NULL;
-
- if (is_movable)
- {
- active_folder_view = active_panel->getRootFolder();
- is_movable = active_folder_view != NULL;
- }
-
- if (is_movable && use_filter)
- {
- // Check whether the folder being dragged from active inventory panel
- // passes the filter of the destination panel.
- is_movable = check_category(model, cat_id, active_panel, filter);
- }
- }
- }
- //
- //--------------------------------------------------------------------------------
-
- accept = is_movable;
-
- if (accept && drop)
- {
- // Dropping in or out of marketplace needs (sometimes) confirmation
- if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings))
- {
- if (move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(cat_id) ||
- LLMarketplaceData::instance().isListedAndActive(cat_id)))
- {
- if (LLMarketplaceData::instance().isListed(cat_id) || LLMarketplaceData::instance().isVersionFolder(cat_id))
- {
- // Move the active version folder or listing folder itself outside marketplace listings will unlist the listing so ask that question specifically
- LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat));
- }
- else
- {
- // Any other case will simply modify but not unlist an active listed listing
- LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat));
- }
- return true;
- }
- if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isVersionFolder(cat_id))
- {
- // Moving the version folder from its location will deactivate it. Ask confirmation.
- LLNotificationsUtil::add("ConfirmMerchantClearVersion", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat));
- return true;
- }
- if (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID))
- {
- // Moving something in an active listed listing will modify it. Ask confirmation.
- LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat));
- return true;
- }
- if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isListed(cat_id))
- {
- // Moving a whole listing folder will result in archival of SLM data. Ask confirmation.
- LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat));
- return true;
- }
- if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings)
- {
- LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat));
- return true;
- }
- }
- // Look for any gestures and deactivate them
- if (move_is_into_trash)
- {
- for (S32 i=0; i < descendent_items.size(); i++)
- {
- LLInventoryItem* item = descendent_items[i];
- if (item->getType() == LLAssetType::AT_GESTURE
- && LLGestureMgr::instance().isGestureActive(item->getUUID()))
- {
- LLGestureMgr::instance().deactivateGesture(item->getUUID());
- }
- }
- }
-
- if (mUUID == my_outifts_id)
- {
- // Category can contains objects,
- // create a new folder and populate it with links to original objects
- dropToMyOutfits(inv_cat, cb);
- }
- // if target is current outfit folder we use link
- else if (move_is_into_current_outfit &&
- (inv_cat->getPreferredType() == LLFolderType::FT_NONE ||
- inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT))
- {
- // traverse category and add all contents to currently worn.
- bool append = true;
- LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append);
- if (cb) cb->fire(inv_cat->getUUID());
- }
- else if (move_is_into_marketplacelistings)
- {
- move_folder_to_marketplacelistings(inv_cat, mUUID);
- if (cb) cb->fire(inv_cat->getUUID());
- }
- else
- {
- if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX)))
- {
- set_dad_inbox_object(cat_id);
- }
-
- // Reparent the folder and restamp children if it's moving
- // into trash.
- LLInvFVBridge::changeCategoryParent(
- model,
- (LLViewerInventoryCategory*)inv_cat,
- mUUID,
- move_is_into_trash);
- if (cb) cb->fire(inv_cat->getUUID());
- }
- if (move_is_from_marketplacelistings)
- {
- // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder)
- if (from_folder_uuid == marketplacelistings_id)
- {
- // Clear the folder from the marketplace in case it is a listing folder
- if (LLMarketplaceData::instance().isListed(cat_id))
- {
- LLMarketplaceData::instance().clearListing(cat_id);
- }
- }
- else
- {
- // If we move from within an active (listed) listing, checks that it's still valid, if not, unlist
- LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid);
- if (version_folder_id.notNull())
- {
- LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
- version_folder_id,
- [version_folder_id](bool result)
- {
- if (!result)
- {
- LLMarketplaceData::instance().activateListing(version_folder_id, false);
- }
- }
- );
- }
- // In all cases, update the listing we moved from so suffix are updated
- update_marketplace_category(from_folder_uuid);
- if (cb) cb->fire(inv_cat->getUUID());
- }
- }
- }
- }
- else if (LLToolDragAndDrop::SOURCE_WORLD == source)
- {
- if (move_is_into_marketplacelistings)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
- accept = false;
- }
- else
- {
- // Todo: fix me. moving from task inventory doesn't have a completion callback,
- // yet making a copy creates new item id so this doesn't work right
- std::function<void(S32, void*, const LLMoveInv*)> callback = [cb](S32, void*, const LLMoveInv* move_inv) mutable
- {
- two_uuids_list_t::const_iterator move_it;
- for (move_it = move_inv->mMoveList.begin();
- move_it != move_inv->mMoveList.end();
- ++move_it)
- {
- if (cb)
- {
- cb->fire(move_it->second);
- }
- }
- };
- accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, callback, NULL, filter);
- }
- }
- else if (LLToolDragAndDrop::SOURCE_LIBRARY == source)
- {
- if (move_is_into_marketplacelistings)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
- accept = false;
- }
- else
- {
- // Accept folders that contain complete outfits.
- accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id);
- }
-
- if (accept && drop)
- {
- LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false);
- }
- }
-
- return accept;
-}
-
-void warn_move_inventory(LLViewerObject* object, std::shared_ptr<LLMoveInv> move_inv)
-{
- const char* dialog = NULL;
- if (object->flagScripted())
- {
- dialog = "MoveInventoryFromScriptedObject";
- }
- else
- {
- dialog = "MoveInventoryFromObject";
- }
-
- static LLNotificationPtr notification_ptr;
- static std::shared_ptr<LLMoveInv> inv_ptr;
-
- // Notification blocks user from interacting with inventories so everything that comes after first message
- // is part of this message - don'r show it again
- // Note: workaround for MAINT-5495 untill proper refactoring and warning system for Drag&Drop can be made.
- if (notification_ptr == NULL
- || !notification_ptr->isActive()
- || LLNotificationsUtil::find(notification_ptr->getID()) == NULL
- || inv_ptr->mCategoryID != move_inv->mCategoryID
- || inv_ptr->mObjectID != move_inv->mObjectID)
- {
- notification_ptr = LLNotificationsUtil::add(dialog, LLSD(), LLSD(), boost::bind(move_task_inventory_callback, _1, _2, move_inv));
- inv_ptr = move_inv;
- }
- else
- {
- // Notification is alive and not responded, operating inv_ptr should be safe so attach new data
- two_uuids_list_t::iterator move_it;
- for (move_it = move_inv->mMoveList.begin();
- move_it != move_inv->mMoveList.end();
- ++move_it)
- {
- inv_ptr->mMoveList.push_back(*move_it);
- }
- move_inv.reset();
- }
-}
-
-// Move/copy all inventory items from the Contents folder of an in-world
-// object to the agent's inventory, inside a given category.
-bool move_inv_category_world_to_agent(const LLUUID& object_id,
- const LLUUID& category_id,
- bool drop,
- std::function<void(S32, void*, const LLMoveInv*)> callback,
- void* user_data,
- LLInventoryFilter* filter)
-{
- // Make sure the object exists. If we allowed dragging from
- // anonymous objects, it would be possible to bypass
- // permissions.
- // content category has same ID as object itself
- LLViewerObject* object = gObjectList.findObject(object_id);
- if(!object)
- {
- LL_INFOS() << "Object not found for drop." << LL_ENDL;
- return false;
- }
-
- // this folder is coming from an object, as there is only one folder in an object, the root,
- // we need to collect the entire contents and handle them as a group
- LLInventoryObject::object_list_t inventory_objects;
- object->getInventoryContents(inventory_objects);
-
- if (inventory_objects.empty())
- {
- LL_INFOS() << "Object contents not found for drop." << LL_ENDL;
- return false;
- }
-
- bool accept = false;
- bool is_move = false;
- bool use_filter = false;
- if (filter)
- {
- U64 filter_types = filter->getFilterTypes();
- use_filter = filter_types && (filter_types&LLInventoryFilter::FILTERTYPE_DATE || (filter_types&LLInventoryFilter::FILTERTYPE_OBJECT)==0);
- }
-
- // coming from a task. Need to figure out if the person can
- // move/copy this item.
- LLInventoryObject::object_list_t::iterator it = inventory_objects.begin();
- LLInventoryObject::object_list_t::iterator end = inventory_objects.end();
- for ( ; it != end; ++it)
- {
- LLInventoryItem* item = dynamic_cast<LLInventoryItem*>(it->get());
- if (!item)
- {
- LL_WARNS() << "Invalid inventory item for drop" << LL_ENDL;
- continue;
- }
-
- // coming from a task. Need to figure out if the person can
- // move/copy this item.
- LLPermissions perm(item->getPermissions());
- if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
- && perm.allowTransferTo(gAgent.getID())))
-// || gAgent.isGodlike())
- {
- accept = true;
- }
- else if(object->permYouOwner())
- {
- // If the object cannot be copied, but the object the
- // inventory is owned by the agent, then the item can be
- // moved from the task to agent inventory.
- is_move = true;
- accept = true;
- }
-
- if (accept && use_filter)
- {
- accept = filter->check(item);
- }
-
- if (!accept)
- {
- break;
- }
- }
-
- if(drop && accept)
- {
- it = inventory_objects.begin();
- std::shared_ptr<LLMoveInv> move_inv(new LLMoveInv);
- move_inv->mObjectID = object_id;
- move_inv->mCategoryID = category_id;
- move_inv->mCallback = callback;
- move_inv->mUserData = user_data;
-
- for ( ; it != end; ++it)
- {
- two_uuids_t two(category_id, (*it)->getUUID());
- move_inv->mMoveList.push_back(two);
- }
-
- if(is_move)
- {
- // Callback called from within here.
- warn_move_inventory(object, move_inv);
- }
- else
- {
- LLNotification::Params params("MoveInventoryFromObject");
- params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
- LLNotifications::instance().forceResponse(params, 0);
- }
- }
- return accept;
-}
-
-void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer)
-{
- // Bail out immediately if no descendents
- if( mComplete.empty() )
- {
- LL_WARNS() << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << LL_ENDL;
- if (clear_observer)
- {
- gInventory.removeObserver(this);
- delete this;
- }
- return;
- }
-
- // Copy the list of complete fetched folders while "this" is still valid
- uuid_vec_t completed_folder = mComplete;
-
- // Clean up, and remove this as an observer now since recursive calls
- // could notify observers and throw us into an infinite loop.
- if (clear_observer)
- {
- gInventory.removeObserver(this);
- delete this;
- }
-
- for (uuid_vec_t::iterator current_folder = completed_folder.begin(); current_folder != completed_folder.end(); ++current_folder)
- {
- // Get the information on the fetched folder items and subfolders and fetch those
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array);
-
- S32 item_count(0);
- if( item_array )
- {
- item_count = item_array->size();
- }
-
- S32 cat_count(0);
- if( cat_array )
- {
- cat_count = cat_array->size();
- }
-
- // Move to next if current folder empty
- if ((item_count == 0) && (cat_count == 0))
- {
- continue;
- }
-
- uuid_vec_t ids;
- LLRightClickInventoryFetchObserver* outfit = NULL;
- LLRightClickInventoryFetchDescendentsObserver* categories = NULL;
-
- // Fetch the items
- if (item_count)
- {
- for (S32 i = 0; i < item_count; ++i)
- {
- ids.push_back(item_array->at(i)->getUUID());
- }
- outfit = new LLRightClickInventoryFetchObserver(ids);
- }
- // Fetch the subfolders
- if (cat_count)
- {
- for (S32 i = 0; i < cat_count; ++i)
- {
- ids.push_back(cat_array->at(i)->getUUID());
- }
- categories = new LLRightClickInventoryFetchDescendentsObserver(ids);
- }
-
- // Perform the item fetch
- if (outfit)
- {
- outfit->startFetch();
- outfit->execute(); // Not interested in waiting and this will be right 99% of the time.
- delete outfit;
-//Uncomment the following code for laggy Inventory UI.
- /*
- if (outfit->isFinished())
- {
- // everything is already here - call done.
- outfit->execute();
- delete outfit;
- }
- else
- {
- // it's all on its way - add an observer, and the inventory
- // will call done for us when everything is here.
- gInventory.addObserver(outfit);
- }
- */
- }
- // Perform the subfolders fetch : this is where we truly recurse down the folder hierarchy
- if (categories)
- {
- categories->startFetch();
- if (categories->isFinished())
- {
- // everything is already here - call done.
- categories->execute();
- delete categories;
- }
- else
- {
- // it's all on its way - add an observer, and the inventory
- // will call done for us when everything is here.
- gInventory.addObserver(categories);
- }
- }
- }
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLInventoryWearObserver
-//
-// Observer for "copy and wear" operation to support knowing
-// when the all of the contents have been added to inventory.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLInventoryCopyAndWearObserver : public LLInventoryObserver
-{
-public:
- LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count, bool folder_added=false, bool replace=false) :
- mCatID(cat_id), mContentsCount(count), mFolderAdded(folder_added), mReplace(replace){}
- virtual ~LLInventoryCopyAndWearObserver() {}
- virtual void changed(U32 mask);
-
-protected:
- LLUUID mCatID;
- int mContentsCount;
- bool mFolderAdded;
- bool mReplace;
-};
-
-
-
-void LLInventoryCopyAndWearObserver::changed(U32 mask)
-{
- if((mask & (LLInventoryObserver::ADD)) != 0)
- {
- if (!mFolderAdded)
- {
- const std::set<LLUUID>& changed_items = gInventory.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)
- {
- if ((*id_it) == mCatID)
- {
- mFolderAdded = true;
- break;
- }
- }
- }
-
- if (mFolderAdded)
- {
- LLViewerInventoryCategory* category = gInventory.getCategory(mCatID);
- if (NULL == category)
- {
- LL_WARNS() << "gInventory.getCategory(" << mCatID
- << ") was NULL" << LL_ENDL;
- }
- else
- {
- if (category->getDescendentCount() ==
- mContentsCount)
- {
- gInventory.removeObserver(this);
- LLAppearanceMgr::instance().wearInventoryCategory(category, false, !mReplace);
- delete this;
- }
- }
- }
-
- }
-}
-
-
-
-void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("open" == action)
- {
- LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mInventoryPanel.get()->getItemByID(mUUID));
- if (f)
- {
- f->toggleOpen();
- }
-
- return;
- }
- else if ("thumbnail" == action)
- {
- LLSD data(mUUID);
- LLFloaterReg::showInstance("change_item_thumbnail", data);
- return;
- }
- else if ("paste" == action)
- {
- pasteFromClipboard();
- return;
- }
- else if ("paste_link" == action)
- {
- pasteLinkFromClipboard();
- return;
- }
- else if ("properties" == action)
- {
- showProperties();
- return;
- }
- else if ("replaceoutfit" == action)
- {
- modifyOutfit(false);
- return;
- }
- else if ("addtooutfit" == action)
- {
- modifyOutfit(true);
- return;
- }
- else if ("show_in_main_panel" == action)
- {
- LLInventoryPanel::openInventoryPanelAndSetSelection(true, mUUID, true);
- return;
- }
- else if ("cut" == action)
- {
- cutToClipboard();
- return;
- }
- else if ("copy" == action)
- {
- copyToClipboard();
- return;
- }
- else if ("removefromoutfit" == action)
- {
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLViewerInventoryCategory* cat = getCategory();
- if(!cat) return;
-
- LLAppearanceMgr::instance().takeOffOutfit( cat->getLinkedUUID() );
- return;
- }
- else if ("copyoutfittoclipboard" == action)
- {
- copyOutfitToClipboard();
- }
- else if ("purge" == action)
- {
- purgeItem(model, mUUID);
- return;
- }
- else if ("restore" == action)
- {
- restoreItem();
- return;
- }
- else if ("marketplace_list" == action)
- {
- if (depth_nesting_in_marketplace(mUUID) == 1)
- {
- LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID);
- mMessage = "";
-
- LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
- version_folder_id,
- [this](bool result)
- {
- // todo: might need to ensure bridge/mUUID exists or this will cause crashes
- if (!result)
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = mMessage;
- LLNotificationsUtil::add("MerchantListingFailed", subs);
- }
- else
- {
- LLMarketplaceData::instance().activateListing(mUUID, true);
- }
- },
- boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3)
- );
- }
- return;
- }
- else if ("marketplace_activate" == action)
- {
- if (depth_nesting_in_marketplace(mUUID) == 2)
- {
- mMessage = "";
-
- LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
- mUUID,
- [this](bool result)
- {
- if (!result)
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = mMessage;
- LLNotificationsUtil::add("MerchantFolderActivationFailed", subs);
- }
- else
- {
- LLInventoryCategory* category = gInventory.getCategory(mUUID);
- LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID);
- }
- },
- boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),
- false,
- 2);
- }
- return;
- }
- else if ("marketplace_unlist" == action)
- {
- if (depth_nesting_in_marketplace(mUUID) == 1)
- {
- LLMarketplaceData::instance().activateListing(mUUID,false,1);
- }
- return;
- }
- else if ("marketplace_deactivate" == action)
- {
- if (depth_nesting_in_marketplace(mUUID) == 2)
- {
- LLInventoryCategory* category = gInventory.getCategory(mUUID);
- LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), LLUUID::null, 1);
- }
- return;
- }
- else if ("marketplace_create_listing" == action)
- {
- mMessage = "";
-
- // first run vithout fix_hierarchy, second run with fix_hierarchy
- LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
- mUUID,
- [this](bool result)
- {
- if (!result)
- {
- mMessage = "";
-
- LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
- mUUID,
- [this](bool result)
- {
- if (result)
- {
- LLNotificationsUtil::add("MerchantForceValidateListing");
- LLMarketplaceData::instance().createListing(mUUID);
- }
- else
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = mMessage;
- LLNotificationsUtil::add("MerchantListingFailed", subs);
- }
- },
- boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),
- true);
- }
- else
- {
- LLMarketplaceData::instance().createListing(mUUID);
- }
- },
- boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),
- false);
-
- return;
- }
- else if ("marketplace_disassociate_listing" == action)
- {
- LLMarketplaceData::instance().clearListing(mUUID);
- return;
- }
- else if ("marketplace_get_listing" == action)
- {
- // This is used only to exercise the SLM API but won't be shown to end users
- LLMarketplaceData::instance().getListing(mUUID);
- return;
- }
- else if ("marketplace_associate_listing" == action)
- {
- LLFloaterAssociateListing::show(mUUID);
- return;
- }
- else if ("marketplace_check_listing" == action)
- {
- LLSD data(mUUID);
- LLFloaterReg::showInstance("marketplace_validation", data);
- return;
- }
- else if ("marketplace_edit_listing" == action)
- {
- std::string url = LLMarketplaceData::instance().getListingURL(mUUID);
- if (!url.empty())
- {
- LLUrlAction::openURL(url);
- }
- return;
- }
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- else if ("delete_system_folder" == action)
- {
- removeSystemFolder();
- }
-#endif
- else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action))
- {
- LLInventoryCategory * cat = gInventory.getCategory(mUUID);
- if (!cat) return;
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action)));
- }
-}
-
-void LLFolderBridge::gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level)
-{
- if (log_level >= LLError::LEVEL_ERROR)
- {
- if (!mMessage.empty())
- {
- // Currently, we do not gather all messages as it creates very long alerts
- // Users can get to the whole list of errors on a listing using the "Check for Errors" audit button or "Check listing" right click menu
- //mMessage += "\n";
- return;
- }
- // Take the leading spaces out...
- std::string::size_type start = message.find_first_not_of(" ");
- // Append the message
- mMessage += message.substr(start, message.length() - start);
- }
-}
-
-void LLFolderBridge::copyOutfitToClipboard()
-{
- std::string text;
-
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array);
-
- S32 item_count(0);
- if( item_array )
- {
- item_count = item_array->size();
- }
-
- if (item_count)
- {
- for (S32 i = 0; i < item_count;)
- {
- LLSD uuid =item_array->at(i)->getUUID();
- LLViewerInventoryItem* item = gInventory.getItem(uuid);
-
- i++;
- if (item != NULL)
- {
- // Append a newline to all but the last line
- text += i != item_count ? item->getName() + "\n" : item->getName();
- }
- }
- }
-
- LLClipboard::instance().copyToClipboard(utf8str_to_wstring(text),0,text.size());
-}
-
-void LLFolderBridge::openItem()
-{
- LL_DEBUGS() << "LLFolderBridge::openItem()" << LL_ENDL;
-
- LLInventoryPanel* panel = mInventoryPanel.get();
- if (!panel)
- {
- return;
- }
- LLInventoryModel* model = getInventoryModel();
- if (!model)
- {
- return;
- }
- if (mUUID.isNull())
- {
- return;
- }
- panel->onFolderOpening(mUUID);
- bool fetching_inventory = model->fetchDescendentsOf(mUUID);
- // Only change folder type if we have the folder contents.
- if (!fetching_inventory)
- {
- // Disabling this for now, it's causing crash when new items are added to folders
- // since folder type may change before new item item has finished processing.
- // determineFolderType();
- }
-}
-
-void LLFolderBridge::closeItem()
-{
- determineFolderType();
-}
-
-void LLFolderBridge::determineFolderType()
-{
- if (isUpToDate())
- {
- LLInventoryModel* model = getInventoryModel();
- LLViewerInventoryCategory* category = model->getCategory(mUUID);
- if (category)
- {
- category->determineFolderType();
- }
- }
-}
-
-bool LLFolderBridge::isItemRenameable() const
-{
- return get_is_category_renameable(getInventoryModel(), mUUID);
-}
-
-void LLFolderBridge::restoreItem()
-{
- LLViewerInventoryCategory* cat;
- cat = (LLViewerInventoryCategory*)getCategory();
- if(cat)
- {
- LLInventoryModel* model = getInventoryModel();
- const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType()));
- // do not restamp children on restore
- LLInvFVBridge::changeCategoryParent(model, cat, new_parent, false);
- }
-}
-
-LLFolderType::EType LLFolderBridge::getPreferredType() const
-{
- LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
- LLViewerInventoryCategory* cat = getCategory();
- if(cat)
- {
- preferred_type = cat->getPreferredType();
- }
-
- return preferred_type;
-}
-
-// Icons for folders are based on the preferred type
-LLUIImagePtr LLFolderBridge::getIcon() const
-{
- return getFolderIcon(false);
-}
-
-LLUIImagePtr LLFolderBridge::getIconOpen() const
-{
- return getFolderIcon(true);
-}
-
-LLUIImagePtr LLFolderBridge::getFolderIcon(bool is_open) const
-{
- LLFolderType::EType preferred_type = getPreferredType();
- return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open));
-}
-
-// static : use by LLLinkFolderBridge to get the closed type icons
-LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type)
-{
- return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, false));
-}
-
-LLUIImagePtr LLFolderBridge::getIconOverlay() const
-{
- if (getInventoryObject() && getInventoryObject()->getIsLinkType())
- {
- return LLUI::getUIImage("Inv_Link");
- }
- return NULL;
-}
-
-bool LLFolderBridge::renameItem(const std::string& new_name)
-{
-
- LLScrollOnRenameObserver *observer = new LLScrollOnRenameObserver(mUUID, mRoot);
- gInventory.addObserver(observer);
-
- rename_category(getInventoryModel(), mUUID, new_name);
-
- // return false because we either notified observers (& therefore
- // rebuilt) or we didn't update.
- return false;
-}
-
-bool LLFolderBridge::removeItem()
-{
- if(!isItemRemovable())
- {
- return false;
- }
- const LLViewerInventoryCategory *cat = getCategory();
-
- LLSD payload;
- LLSD args;
- args["FOLDERNAME"] = cat->getName();
-
- LLNotification::Params params("ConfirmDeleteProtectedCategory");
- params.payload(payload).substitutions(args).functor.function(boost::bind(&LLFolderBridge::removeItemResponse, this, _1, _2));
- LLNotifications::instance().forceResponse(params, 0);
- return true;
-}
-
-
-bool LLFolderBridge::removeSystemFolder()
-{
- const LLViewerInventoryCategory *cat = getCategory();
- if (!LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
- {
- return false;
- }
-
- LLSD payload;
- LLSD args;
- args["FOLDERNAME"] = cat->getName();
-
- LLNotification::Params params("ConfirmDeleteProtectedCategory");
- params.payload(payload).substitutions(args).functor.function(boost::bind(&LLFolderBridge::removeItemResponse, this, _1, _2));
- {
- LLNotifications::instance().add(params);
- }
- return true;
-}
-
-bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotification::getSelectedOption(notification, response);
-
- // if they choose delete, do it. Otherwise, don't do anything
- if(option == 0)
- {
- // move it to the trash
- LLPreview::hide(mUUID);
- getInventoryModel()->removeCategory(mUUID);
- return true;
- }
- return false;
-}
-
-//Recursively update the folder's creation date
-void LLFolderBridge::updateHierarchyCreationDate(time_t date)
-{
- if(getCreationDate() < date)
- {
- setCreationDate(date);
- if(mParent)
- {
- static_cast<LLFolderBridge *>(mParent)->updateHierarchyCreationDate(date);
- }
- }
-}
-
-void LLFolderBridge::pasteFromClipboard()
-{
- LLInventoryModel* model = getInventoryModel();
- if (model && isClipboardPasteable())
- {
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const bool paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
-
- bool cut_from_marketplacelistings = false;
- if (LLClipboard::instance().isCutMode())
- {
- //Items are not removed from folder on "cut", so we need update listing folder on "paste" operation
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
- for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter)
- {
- const LLUUID& item_id = (*iter);
- if(gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) ||
- LLMarketplaceData::instance().isListedAndActive(item_id)))
- {
- cut_from_marketplacelistings = true;
- break;
- }
- }
- }
- if (cut_from_marketplacelistings || (paste_into_marketplacelistings && !LLMarketplaceData::instance().isListed(mUUID) && LLMarketplaceData::instance().isInActiveFolder(mUUID)))
- {
- // Prompt the user if pasting in a marketplace active version listing (note that pasting right under the listing folder root doesn't need a prompt)
- LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_pasteFromClipboard, this, _1, _2));
- }
- else
- {
- // Otherwise just do the paste
- perform_pasteFromClipboard();
- }
- }
-}
-
-// Callback for pasteFromClipboard if DAMA required...
-void LLFolderBridge::callback_pasteFromClipboard(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option == 0) // YES
- {
- std::vector<LLUUID> objects;
- std::set<LLUUID> parent_folders;
- LLClipboard::instance().pasteFromClipboard(objects);
- for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter)
- {
- const LLInventoryObject* obj = gInventory.getObject(*iter);
- parent_folders.insert(obj->getParentUUID());
- }
- perform_pasteFromClipboard();
- for (std::set<LLUUID>::const_iterator iter = parent_folders.begin(); iter != parent_folders.end(); ++iter)
- {
- gInventory.addChangedMask(LLInventoryObserver::STRUCTURE, *iter);
- }
-
- }
-}
-
-void LLFolderBridge::perform_pasteFromClipboard()
-{
- LLInventoryModel* model = getInventoryModel();
- if (model && isClipboardPasteable())
- {
- const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
- const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
-
- const bool move_is_into_current_outfit = (mUUID == current_outfit_id);
- const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
- const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
- const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
- const bool move_is_into_favorites = (mUUID == favorites_id);
- const bool move_is_into_lost_and_found = model->isObjectDescendentOf(mUUID, lost_and_found_id);
-
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
-
- LLPointer<LLInventoryCallback> cb = NULL;
- LLInventoryPanel* panel = mInventoryPanel.get();
- if (panel->getRootFolder()->isSingleFolderMode() && panel->getRootFolderID() == mUUID)
- {
- cb = new LLPasteIntoFolderCallback(mInventoryPanel);
- }
-
- LLViewerInventoryCategory * dest_folder = getCategory();
- if (move_is_into_marketplacelistings)
- {
- std::string error_msg;
- const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID);
- int index = 0;
- for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter)
- {
- const LLUUID& item_id = (*iter);
- LLInventoryItem *item = model->getItem(item_id);
- LLInventoryCategory *cat = model->getCategory(item_id);
-
- if (item && !can_move_item_to_marketplace(master_folder, dest_folder, item, error_msg, objects.size() - index, true))
- {
- break;
- }
- if (cat && !can_move_folder_to_marketplace(master_folder, dest_folder, cat, error_msg, objects.size() - index, true, true))
- {
- break;
- }
- ++index;
- }
- if (!error_msg.empty())
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = error_msg;
- LLNotificationsUtil::add("MerchantPasteFailed", subs);
- return;
- }
- }
- else
- {
- // Check that all items can be moved into that folder : for the moment, only stock folder mismatch is checked
- for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter)
- {
- const LLUUID& item_id = (*iter);
- LLInventoryItem *item = model->getItem(item_id);
- LLInventoryCategory *cat = model->getCategory(item_id);
-
- if ((item && !dest_folder->acceptItem(item)) || (cat && (dest_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)))
- {
- std::string error_msg = LLTrans::getString("TooltipOutboxMixedStock");
- LLSD subs;
- subs["[ERROR_CODE]"] = error_msg;
- LLNotificationsUtil::add("StockPasteFailed", subs);
- return;
- }
- }
- }
-
- const LLUUID parent_id(mUUID);
-
- for (std::vector<LLUUID>::const_iterator iter = objects.begin();
- iter != objects.end();
- ++iter)
- {
- const LLUUID& item_id = (*iter);
-
- LLInventoryItem *item = model->getItem(item_id);
- LLInventoryObject *obj = model->getObject(item_id);
- if (obj)
- {
-
- if (move_is_into_lost_and_found)
- {
- if (LLAssetType::AT_CATEGORY == obj->getType())
- {
- return;
- }
- }
- if (move_is_into_outfit)
- {
- if (!move_is_into_my_outfits && item && can_move_to_outfit(item, move_is_into_current_outfit))
- {
- dropToOutfit(item, move_is_into_current_outfit, cb);
- }
- else if (move_is_into_my_outfits && LLAssetType::AT_CATEGORY == obj->getType())
- {
- LLInventoryCategory* cat = model->getCategory(item_id);
- U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
- if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear))
- {
- dropToMyOutfits(cat, cb);
- }
- else
- {
- LLNotificationsUtil::add("MyOutfitsPasteFailed");
- }
- }
- else
- {
- LLNotificationsUtil::add("MyOutfitsPasteFailed");
- }
- }
- else if (move_is_into_current_outfit)
- {
- if (item && can_move_to_outfit(item, move_is_into_current_outfit))
- {
- dropToOutfit(item, move_is_into_current_outfit, cb);
- }
- else
- {
- LLNotificationsUtil::add("MyOutfitsPasteFailed");
- }
- }
- else if (move_is_into_favorites)
- {
- if (item && can_move_to_landmarks(item))
- {
- if (LLClipboard::instance().isCutMode())
- {
- LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
- llassert(viitem);
- if (viitem)
- {
- //changeItemParent() implicity calls dirtyFilter
- changeItemParent(model, viitem, parent_id, false);
- if (cb) cb->fire(item_id);
- }
- }
- else
- {
- dropToFavorites(item, cb);
- }
- }
- }
- else if (LLClipboard::instance().isCutMode())
- {
- // Do a move to "paste" a "cut"
- // move_inventory_item() is not enough, as we have to update inventory locally too
- if (LLAssetType::AT_CATEGORY == obj->getType())
- {
- LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id);
- llassert(vicat);
- if (vicat)
- {
- // Clear the cut folder from the marketplace if it is a listing folder
- if (LLMarketplaceData::instance().isListed(item_id))
- {
- LLMarketplaceData::instance().clearListing(item_id);
- }
- if (move_is_into_marketplacelistings)
- {
- move_folder_to_marketplacelistings(vicat, parent_id);
- }
- else
- {
- //changeCategoryParent() implicity calls dirtyFilter
- changeCategoryParent(model, vicat, parent_id, false);
- }
- if (cb) cb->fire(item_id);
- }
- }
- else
- {
- LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
- llassert(viitem);
- if (viitem)
- {
- if (move_is_into_marketplacelistings)
- {
- if (!move_item_to_marketplacelistings(viitem, parent_id))
- {
- // Stop pasting into the marketplace as soon as we get an error
- break;
- }
- }
- else
- {
- //changeItemParent() implicity calls dirtyFilter
- changeItemParent(model, viitem, parent_id, false);
- }
- if (cb) cb->fire(item_id);
- }
- }
- }
- else
- {
- // Do a "copy" to "paste" a regular copy clipboard
- if (LLAssetType::AT_CATEGORY == obj->getType())
- {
- LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id);
- llassert(vicat);
- if (vicat)
- {
- if (move_is_into_marketplacelistings)
- {
- move_folder_to_marketplacelistings(vicat, parent_id, true);
- }
- else
- {
- copy_inventory_category(model, vicat, parent_id);
- }
- if (cb) cb->fire(item_id);
- }
- }
- else
- {
- LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
- llassert(viitem);
- if (viitem)
- {
- if (move_is_into_marketplacelistings)
- {
- if (!move_item_to_marketplacelistings(viitem, parent_id, true))
- {
- // Stop pasting into the marketplace as soon as we get an error
- break;
- }
- if (cb) cb->fire(item_id);
- }
- else if (item->getIsLinkType())
- {
- link_inventory_object(parent_id,
- item_id,
- cb);
- }
- else
- {
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- parent_id,
- std::string(),
- cb);
- }
- }
- }
- }
- }
- }
- // Change mode to paste for next paste
- LLClipboard::instance().setCutMode(false);
- }
-}
-
-void LLFolderBridge::pasteLinkFromClipboard()
-{
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
-
- const bool move_is_into_current_outfit = (mUUID == current_outfit_id);
- const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
- const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
- const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
-
- if (move_is_into_marketplacelistings)
- {
- // Notify user of failure somehow -- play error sound? modal dialog?
- return;
- }
-
- const LLUUID parent_id(mUUID);
-
- std::vector<LLUUID> objects;
- LLClipboard::instance().pasteFromClipboard(objects);
-
- LLPointer<LLInventoryCallback> cb = NULL;
- LLInventoryPanel* panel = mInventoryPanel.get();
- if (panel->getRootFolder()->isSingleFolderMode())
- {
- cb = new LLPasteIntoFolderCallback(mInventoryPanel);
- }
-
- for (std::vector<LLUUID>::const_iterator iter = objects.begin();
- iter != objects.end();
- ++iter)
- {
- const LLUUID &object_id = (*iter);
- if (move_is_into_current_outfit || move_is_into_outfit)
- {
- LLInventoryItem *item = model->getItem(object_id);
- if (item && can_move_to_outfit(item, move_is_into_current_outfit))
- {
- dropToOutfit(item, move_is_into_current_outfit, cb);
- }
- }
- else if (LLConstPointer<LLInventoryObject> obj = model->getObject(object_id))
- {
- link_inventory_object(parent_id, obj, cb);
- }
- }
- // Change mode to paste for next paste
- LLClipboard::instance().setCutMode(false);
- }
-}
-
-void LLFolderBridge::staticFolderOptionsMenu()
-{
- LLFolderBridge* selfp = sSelf.get();
-
- if (selfp && selfp->mRoot)
- {
- selfp->mRoot->updateMenu();
- }
-}
-
-bool LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- model->collectDescendentsIf(mUUID,
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_type);
- return !item_array.empty();
-}
-
-void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items)
-{
- LLInventoryModel* model = getInventoryModel();
- llassert(model != NULL);
-
- const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
- const LLUUID &favorites = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
- const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
-
- if (outfits_id == mUUID)
- {
- items.push_back(std::string("New Outfit"));
- }
-
- if (lost_and_found_id == mUUID)
- {
- // This is the lost+found folder.
- items.push_back(std::string("Empty Lost And Found"));
-
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array);
- // Enable Empty menu item only when there is something to act upon.
- if (0 == cat_array->size() && 0 == item_array->size())
- {
- disabled_items.push_back(std::string("Empty Lost And Found"));
- }
-
- disabled_items.push_back(std::string("New Folder"));
- disabled_items.push_back(std::string("upload_def"));
- disabled_items.push_back(std::string("create_new"));
- }
- if (favorites == mUUID)
- {
- disabled_items.push_back(std::string("New Folder"));
- }
- if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- if (LLMarketplaceData::instance().isUpdating(mUUID))
- {
- disabled_items.push_back(std::string("New Folder"));
- disabled_items.push_back(std::string("Rename"));
- disabled_items.push_back(std::string("Cut"));
- disabled_items.push_back(std::string("Copy"));
- disabled_items.push_back(std::string("Paste"));
- disabled_items.push_back(std::string("Delete"));
- }
- }
- if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- disabled_items.push_back(std::string("New Folder"));
- disabled_items.push_back(std::string("upload_def"));
- disabled_items.push_back(std::string("create_new"));
- }
- if (marketplace_listings_id == mUUID)
- {
- disabled_items.push_back(std::string("New Folder"));
- disabled_items.push_back(std::string("Rename"));
- disabled_items.push_back(std::string("Cut"));
- disabled_items.push_back(std::string("Delete"));
- }
-
- if (isPanelActive("Favorite Items"))
- {
- disabled_items.push_back(std::string("Delete"));
- }
- if(trash_id == mUUID)
- {
- bool is_recent_panel = isPanelActive("Recent Items");
-
- // This is the trash.
- items.push_back(std::string("Empty Trash"));
-
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array);
- LLViewerInventoryCategory *trash = getCategory();
- // Enable Empty menu item only when there is something to act upon.
- // Also don't enable menu if folder isn't fully fetched
- if ((0 == cat_array->size() && 0 == item_array->size())
- || is_recent_panel
- || !trash
- || trash->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN
- || trash->getDescendentCount() == LLViewerInventoryCategory::VERSION_UNKNOWN
- || gAgentAvatarp->hasAttachmentsInTrash())
- {
- disabled_items.push_back(std::string("Empty Trash"));
- }
-
- items.push_back(std::string("thumbnail"));
- }
- else if(isItemInTrash())
- {
- // This is a folder in the trash.
- items.clear(); // clear any items that used to exist
- addTrashContextMenuOptions(items, disabled_items);
- }
- else if(isAgentInventory()) // do not allow creating in library
- {
- LLViewerInventoryCategory *cat = getCategory();
- // BAP removed protected check to re-enable standard ops in untyped folders.
- // Not sure what the right thing is to do here.
- if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT))
- {
- if (!isInboxFolder() // don't allow creation in inbox
- && outfits_id != mUUID)
- {
- bool menu_items_added = false;
- // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
- if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
- {
- items.push_back(std::string("New Folder"));
- menu_items_added = true;
- }
- if (!isMarketplaceListingsFolder())
- {
- items.push_back(std::string("upload_def"));
- items.push_back(std::string("create_new"));
- items.push_back(std::string("New Script"));
- items.push_back(std::string("New Note"));
- items.push_back(std::string("New Gesture"));
- items.push_back(std::string("New Material"));
- items.push_back(std::string("New Clothes"));
- items.push_back(std::string("New Body Parts"));
- items.push_back(std::string("New Settings"));
- if (!LLEnvironment::instance().isInventoryEnabled())
- {
- disabled_items.push_back("New Settings");
- }
- }
- else
- {
- items.push_back(std::string("New Listing Folder"));
- }
- if (menu_items_added)
- {
- items.push_back(std::string("Create Separator"));
- }
- }
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- // Want some but not all of the items from getClipboardEntries for outfits.
- if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))
- {
- items.push_back(std::string("Rename"));
- items.push_back(std::string("thumbnail"));
-
- addDeleteContextMenuOptions(items, disabled_items);
- // EXT-4030: disallow deletion of currently worn outfit
- const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink();
- if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory()))
- {
- disabled_items.push_back(std::string("Delete"));
- }
- }
- }
-
- if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID)
- {
- items.push_back(std::string("Copy outfit list to clipboard"));
- addOpenFolderMenuOptions(flags, items);
- }
-
- //Added by aura to force inventory pull on right-click to display folder options correctly. 07-17-06
- mCallingCards = mWearables = false;
-
- LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
- if (checkFolderForContentsOfType(model, is_callingcard))
- {
- mCallingCards=true;
- }
-
- LLFindWearables is_wearable;
- LLIsType is_object( LLAssetType::AT_OBJECT );
- LLIsType is_gesture( LLAssetType::AT_GESTURE );
-
- if (checkFolderForContentsOfType(model, is_wearable) ||
- checkFolderForContentsOfType(model, is_object) ||
- checkFolderForContentsOfType(model, is_gesture) )
- {
- mWearables=true;
- }
- }
- else
- {
- // Mark wearables and allow copy from library
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- const LLInventoryCategory* category = model->getCategory(mUUID);
- if (!category) return;
- LLFolderType::EType type = category->getPreferredType();
- const bool is_system_folder = LLFolderType::lookupIsProtectedType(type);
-
- LLFindWearables is_wearable;
- LLIsType is_object(LLAssetType::AT_OBJECT);
- LLIsType is_gesture(LLAssetType::AT_GESTURE);
-
- if (checkFolderForContentsOfType(model, is_wearable) ||
- checkFolderForContentsOfType(model, is_object) ||
- checkFolderForContentsOfType(model, is_gesture))
- {
- mWearables = true;
- }
-
- if (!is_system_folder)
- {
- items.push_back(std::string("Copy"));
- if (!isItemCopyable())
- {
- // For some reason there are items in library that can't be copied directly
- disabled_items.push_back(std::string("Copy"));
- }
- }
- }
-
- // Preemptively disable system folder removal if more than one item selected.
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Delete System Folder"));
- }
-
- if (isAgentInventory() && !isMarketplaceListingsFolder())
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
- }
-
-
-
- // Add menu items that are dependent on the contents of the folder.
- LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID);
- if (category && (marketplace_listings_id != mUUID))
- {
- uuid_vec_t folders;
- folders.push_back(category->getUUID());
-
- sSelf = getHandle();
- LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders);
- fetch->startFetch();
- if (fetch->isFinished())
- {
- // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down
- // This saves lots of time as buildContextMenu() is called a lot
- delete fetch;
- buildContextMenuFolderOptions(flags, items, disabled_items);
- }
- else
- {
- // it's all on its way - add an observer, and the inventory will call done for us when everything is here.
- gInventory.addObserver(fetch);
- }
- }
-}
-
-void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items)
-{
- // Build folder specific options back up
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
-
- const LLInventoryCategory* category = model->getCategory(mUUID);
- if(!category) return;
-
- const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- if ((trash_id == mUUID) || isItemInTrash())
- {
- addOpenFolderMenuOptions(flags, items);
- return;
- }
-
- if (!canMenuDelete())
- {
- disabled_items.push_back(std::string("Delete"));
- }
- if (isMarketplaceListingsFolder()) return;
-
- LLFolderType::EType type = category->getPreferredType();
- const bool is_system_folder = LLFolderType::lookupIsProtectedType(type);
- // BAP change once we're no longer treating regular categories as ensembles.
- const bool is_agent_inventory = isAgentInventory();
-
- // Only enable calling-card related options for non-system folders.
- if (!is_system_folder && is_agent_inventory && (mRoot != NULL))
- {
- LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
- if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
- {
- items.push_back(std::string("Calling Card Separator"));
- items.push_back(std::string("Conference Chat Folder"));
- items.push_back(std::string("IM All Contacts In Folder"));
- }
-
- if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren() && (type != LLFolderType::FT_OUTFIT))
- {
- items.push_back(std::string("Ungroup folder items"));
- }
- }
- else
- {
- disabled_items.push_back(std::string("New folder from selected"));
- }
-
- //skip the rest options in single-folder mode
- if (mRoot == NULL)
- {
- return;
- }
-
- addOpenFolderMenuOptions(flags, items);
-
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory)
- {
- items.push_back(std::string("Delete System Folder"));
- }
-#endif
-
- // wearables related functionality for folders.
- //is_wearable
- LLFindWearables is_wearable;
- LLIsType is_object( LLAssetType::AT_OBJECT );
- LLIsType is_gesture( LLAssetType::AT_GESTURE );
-
- if (mWearables ||
- checkFolderForContentsOfType(model, is_wearable) ||
- checkFolderForContentsOfType(model, is_object) ||
- checkFolderForContentsOfType(model, is_gesture) )
- {
- // Only enable add/replace outfit for non-system folders.
- if (!is_system_folder)
- {
- // Adding an outfit onto another (versus replacing) doesn't make sense.
- if (type != LLFolderType::FT_OUTFIT)
- {
- items.push_back(std::string("Add To Outfit"));
- if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID))
- {
- disabled_items.push_back(std::string("Add To Outfit"));
- }
- }
-
- items.push_back(std::string("Replace Outfit"));
- if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID))
- {
- disabled_items.push_back(std::string("Replace Outfit"));
- }
- }
- if (is_agent_inventory)
- {
- items.push_back(std::string("Folder Wearables Separator"));
- // Note: If user tries to unwear "My Inventory", it's going to deactivate everything including gestures
- // Might be safer to disable this for "My Inventory"
- items.push_back(std::string("Remove From Outfit"));
- if (type != LLFolderType::FT_ROOT_INVENTORY // Unless COF is empty, whih shouldn't be, warrantied to have worn items
- && !LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) // expensive from root!
- {
- disabled_items.push_back(std::string("Remove From Outfit"));
- }
- }
- items.push_back(std::string("Outfit Separator"));
-
- }
-}
-
-// Flags unused
-void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- sSelf.markDead();
-
- // fetch contents of this folder, as context menu can depend on contents
- // still, user would have to open context menu again to see the changes
- gInventory.fetchDescendentsOf(getUUID());
-
-
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- LL_DEBUGS() << "LLFolderBridge::buildContextMenu()" << LL_ENDL;
-
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
-
- buildContextMenuOptions(flags, items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-
- // Reposition the menu, in case we're adding items to an existing menu.
- menu.needsArrange();
- menu.arrangeAndClear();
-}
-
-void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items)
-{
- if ((flags & ITEM_IN_MULTI_SELECTION) == 0)
- {
- items.push_back(std::string("open_in_new_window"));
- items.push_back(std::string("Open Folder Separator"));
- items.push_back(std::string("Copy Separator"));
- if(isPanelActive("comb_single_folder_inv"))
- {
- items.push_back(std::string("open_in_current_window"));
- }
- }
-}
-
-bool LLFolderBridge::hasChildren() const
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return false;
- LLInventoryModel::EHasChildren has_children;
- has_children = gInventory.categoryHasChildren(mUUID);
- return has_children != LLInventoryModel::CHILDREN_NO;
-}
-
-bool LLFolderBridge::dragOrDrop(MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- std::string& tooltip_msg)
-{
- LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
-
- static LLPointer<LLInventoryCallback> drop_cb = NULL;
- LLInventoryPanel* panel = mInventoryPanel.get();
- LLToolDragAndDrop* drop_tool = LLToolDragAndDrop::getInstance();
- if (drop
- && panel->getRootFolder()->isSingleFolderMode()
- && panel->getRootFolderID() == mUUID
- && drop_tool->getCargoIndex() == 0)
- {
- drop_cb = new LLPasteIntoFolderCallback(mInventoryPanel);
- }
-
-
- //LL_INFOS() << "LLFolderBridge::dragOrDrop()" << LL_ENDL;
- bool accept = false;
- switch(cargo_type)
- {
- case DAD_TEXTURE:
- case DAD_SOUND:
- case DAD_CALLINGCARD:
- case DAD_LANDMARK:
- case DAD_SCRIPT:
- case DAD_CLOTHING:
- case DAD_OBJECT:
- case DAD_NOTECARD:
- case DAD_BODYPART:
- case DAD_ANIMATION:
- case DAD_GESTURE:
- case DAD_MESH:
- case DAD_SETTINGS:
- case DAD_MATERIAL:
- accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, true, drop_cb);
- break;
- case DAD_LINK:
- // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER.
- // If we have an item of AT_LINK_FOLDER type we should process the linked
- // category being dragged or dropped into folder.
- if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType())
- {
- LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID());
- if (linked_category)
- {
- accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, true, true, drop_cb);
- }
- }
- else
- {
- accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, true, drop_cb);
- }
- break;
- case DAD_CATEGORY:
- if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID))
- {
- accept = false;
- }
- else
- {
- accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg, false, true, drop_cb);
- }
- break;
- case DAD_ROOT_CATEGORY:
- case DAD_NONE:
- break;
- default:
- LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL;
- break;
- }
-
- if (!drop || drop_tool->getCargoIndex() + 1 == drop_tool->getCargoCount())
- {
- drop_cb = NULL;
- }
- return accept;
-}
-
-LLViewerInventoryCategory* LLFolderBridge::getCategory() const
-{
- LLViewerInventoryCategory* cat = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- cat = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- }
- return cat;
-}
-
-
-// static
-void LLFolderBridge::pasteClipboard(void* user_data)
-{
- LLFolderBridge* self = (LLFolderBridge*)user_data;
- if(self) self->pasteFromClipboard();
-}
-
-void LLFolderBridge::createNewShirt(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHIRT);
-}
-
-void LLFolderBridge::createNewPants(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_PANTS);
-}
-
-void LLFolderBridge::createNewShoes(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHOES);
-}
-
-void LLFolderBridge::createNewSocks(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SOCKS);
-}
-
-void LLFolderBridge::createNewJacket(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_JACKET);
-}
-
-void LLFolderBridge::createNewSkirt(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SKIRT);
-}
-
-void LLFolderBridge::createNewGloves(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_GLOVES);
-}
-
-void LLFolderBridge::createNewUndershirt(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_UNDERSHIRT);
-}
-
-void LLFolderBridge::createNewUnderpants(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_UNDERPANTS);
-}
-
-void LLFolderBridge::createNewShape(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHAPE);
-}
-
-void LLFolderBridge::createNewSkin(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SKIN);
-}
-
-void LLFolderBridge::createNewHair(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_HAIR);
-}
-
-void LLFolderBridge::createNewEyes(void* user_data)
-{
- LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_EYES);
-}
-
-EInventorySortGroup LLFolderBridge::getSortGroup() const
-{
- LLFolderType::EType preferred_type = getPreferredType();
-
- if (preferred_type == LLFolderType::FT_TRASH)
- {
- return SG_TRASH_FOLDER;
- }
-
- if(LLFolderType::lookupIsProtectedType(preferred_type))
- {
- return SG_SYSTEM_FOLDER;
- }
-
- return SG_NORMAL_FOLDER;
-}
-
-
-// static
-void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::EType type)
-{
- if(!bridge) return;
- LLUUID parent_id = bridge->getUUID();
- LLAgentWearables::createWearable(type, false, parent_id);
-}
-
-void LLFolderBridge::modifyOutfit(bool append)
-{
- LLInventoryModel* model = getInventoryModel();
- if(!model) return;
- LLViewerInventoryCategory* cat = getCategory();
- if(!cat) return;
-
- // checking amount of items to wear
- U32 max_items = gSavedSettings.getU32("WearFolderLimit");
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(cat->getUUID(),
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- not_worn);
-
- if (items.size() > max_items)
- {
- LLSD args;
- args["AMOUNT"] = llformat("%d", max_items);
- LLNotificationsUtil::add("TooManyWearables", args);
- return;
- }
-
- if (isAgentInventory())
- {
- LLAppearanceMgr::instance().wearInventoryCategory(cat, false, append);
- }
- else
- {
- // Library, we need to copy content first
- LLAppearanceMgr::instance().wearInventoryCategory(cat, true, append);
- }
-}
-
-//static
-void LLFolderBridge::onCanDeleteIdle(void* user_data)
-{
- LLFolderBridge* self = (LLFolderBridge*)user_data;
-
- // we really need proper onidle mechanics that returns available time
- const F32 EXPIRY_SECONDS = 0.008f;
- LLTimer timer;
- timer.setTimerExpirySec(EXPIRY_SECONDS);
-
- LLInventoryModel* model = self->getInventoryModel();
- if (model)
- {
- switch (self->mCanDeleteFolderState)
- {
- case CDS_INIT_FOLDER_CHECK:
- // Can still be expensive, split it further?
- model->collectDescendents(
- self->mUUID,
- self->mFoldersToCheck,
- self->mItemsToCheck,
- LLInventoryModel::EXCLUDE_TRASH);
- self->mCanDeleteFolderState = CDS_PROCESSING_ITEMS;
- break;
-
- case CDS_PROCESSING_ITEMS:
- while (!timer.hasExpired() && !self->mItemsToCheck.empty())
- {
- LLViewerInventoryItem* item = self->mItemsToCheck.back().get();
- if (item)
- {
- if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item))
- {
- if (get_is_item_worn(item))
- {
- // At the moment we disable 'cut' if category has worn items (do we need to?)
- // but allow 'delete' to happen since it will prompt user to detach
- self->mCanCut = false;
- }
- }
-
- if (!item->getIsLinkType() && get_is_item_worn(item))
- {
- self->mCanCut = false;
- }
- }
- self->mItemsToCheck.pop_back();
- }
- self->mCanDeleteFolderState = CDS_PROCESSING_FOLDERS;
- break;
- case CDS_PROCESSING_FOLDERS:
- {
- const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink();
- LLViewerInventoryCategory* outfit_linked_category = base_outfit_link ? base_outfit_link->getLinkedCategory() : nullptr;
-
- while (!timer.hasExpired() && !self->mFoldersToCheck.empty())
- {
- LLViewerInventoryCategory* cat = self->mFoldersToCheck.back().get();
- if (cat)
- {
- const LLFolderType::EType folder_type = cat->getPreferredType();
- if (LLFolderType::lookupIsProtectedType(folder_type))
- {
- self->mCanCut = false;
- self->mCanDelete = false;
- self->completeDeleteProcessing();
- break;
- }
-
- // Can't delete the outfit that is currently being worn.
- if (folder_type == LLFolderType::FT_OUTFIT)
- {
- if (cat == outfit_linked_category)
- {
- self->mCanCut = false;
- self->mCanDelete = false;
- self->completeDeleteProcessing();
- break;
- }
- }
- }
- self->mFoldersToCheck.pop_back();
- }
- }
- self->mCanDeleteFolderState = CDS_DONE;
- break;
- case CDS_DONE:
- self->completeDeleteProcessing();
- break;
- }
- }
-}
-
-bool LLFolderBridge::canMenuDelete()
-{
- LLInventoryModel* model = getInventoryModel();
- if (!model) return false;
- LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- if (!category)
- {
- return false;
- }
-
- S32 version = category->getVersion();
- if (mLastCheckedVersion == version)
- {
- return mCanDelete;
- }
-
- initCanDeleteProcessing(model, version);
- return false;
-}
-
-bool LLFolderBridge::canMenuCut()
-{
- LLInventoryModel* model = getInventoryModel();
- if (!model) return false;
- LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
- if (!category)
- {
- return false;
- }
-
- S32 version = category->getVersion();
- if (mLastCheckedVersion == version)
- {
- return mCanCut;
- }
-
- initCanDeleteProcessing(model, version);
- return false;
-}
-
-void LLFolderBridge::initCanDeleteProcessing(LLInventoryModel* model, S32 version)
-{
- if (mCanDeleteFolderState == CDS_DONE
- || mInProgressVersion != version)
- {
- if (get_is_category_removable(model, mUUID))
- {
- // init recursive check of content
- mInProgressVersion = version;
- mCanCut = true;
- mCanDelete = true;
- mCanDeleteFolderState = CDS_INIT_FOLDER_CHECK;
- mFoldersToCheck.clear();
- mItemsToCheck.clear();
- gIdleCallbacks.addFunction(onCanDeleteIdle, this);
- }
- else
- {
- // no check needed
- mCanDelete = false;
- mCanCut = false;
- mLastCheckedVersion = version;
- mCanDeleteFolderState = CDS_DONE;
- mFoldersToCheck.clear();
- mItemsToCheck.clear();
- }
- }
-}
-
-void LLFolderBridge::completeDeleteProcessing()
-{
- LLInventoryModel* model = getInventoryModel();
- LLViewerInventoryCategory* category = model ? (LLViewerInventoryCategory*)model->getCategory(mUUID) : nullptr;
- if (model && category && category->getVersion() == mInProgressVersion)
- {
- mLastCheckedVersion = mInProgressVersion;
- mCanDeleteFolderState = CDS_DONE;
- gIdleCallbacks.deleteFunction(onCanDeleteIdle, this);
- }
- else
- {
- mCanDelete = false;
- mCanCut = false;
- mLastCheckedVersion = LLViewerInventoryCategory::VERSION_UNKNOWN;
- mCanDeleteFolderState = CDS_DONE;
- }
-
- if (mRoot)
- {
- mRoot->updateMenu();
- }
-}
-
-
-// +=================================================+
-// | LLMarketplaceFolderBridge |
-// +=================================================+
-
-// LLMarketplaceFolderBridge is a specialized LLFolderBridge for use in Marketplace Inventory panels
-LLMarketplaceFolderBridge::LLMarketplaceFolderBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid) :
-LLFolderBridge(inventory, root, uuid)
-{
- m_depth = depth_nesting_in_marketplace(mUUID);
- m_stockCountCache = COMPUTE_STOCK_NOT_EVALUATED;
-}
-
-LLUIImagePtr LLMarketplaceFolderBridge::getIcon() const
-{
- return getMarketplaceFolderIcon(false);
-}
-
-LLUIImagePtr LLMarketplaceFolderBridge::getIconOpen() const
-{
- return getMarketplaceFolderIcon(true);
-}
-
-LLUIImagePtr LLMarketplaceFolderBridge::getMarketplaceFolderIcon(bool is_open) const
-{
- LLFolderType::EType preferred_type = getPreferredType();
- if (!LLMarketplaceData::instance().isUpdating(getUUID()))
- {
- // Skip computation (expensive) if we're waiting for updates. Use the old value in that case.
- m_depth = depth_nesting_in_marketplace(mUUID);
- }
- if ((preferred_type == LLFolderType::FT_NONE) && (m_depth == 2))
- {
- // We override the type when in the marketplace listings folder and only for version folder
- preferred_type = LLFolderType::FT_MARKETPLACE_VERSION;
- }
- return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open));
-}
-
-std::string LLMarketplaceFolderBridge::getLabelSuffix() const
-{
- if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= FOLDER_LOADING_MESSAGE_DELAY)
- {
- return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str());
- }
-
- std::string suffix = "";
- // Listing folder case
- if (LLMarketplaceData::instance().isListed(getUUID()))
- {
- suffix = llformat("%d",LLMarketplaceData::instance().getListingID(getUUID()));
- if (suffix.empty())
- {
- suffix = LLTrans::getString("MarketplaceNoID");
- }
- suffix = " (" + suffix + ")";
- if (LLMarketplaceData::instance().getActivationState(getUUID()))
- {
- suffix += " (" + LLTrans::getString("MarketplaceLive") + ")";
- }
- }
- // Version folder case
- else if (LLMarketplaceData::instance().isVersionFolder(getUUID()))
- {
- suffix += " (" + LLTrans::getString("MarketplaceActive") + ")";
- }
- // Add stock amount
- bool updating = LLMarketplaceData::instance().isUpdating(getUUID());
- if (!updating)
- {
- // Skip computation (expensive) if we're waiting for update anyway. Use the old value in that case.
- m_stockCountCache = compute_stock_count(getUUID());
- }
- if (m_stockCountCache == 0)
- {
- suffix += " (" + LLTrans::getString("MarketplaceNoStock") + ")";
- }
- else if (m_stockCountCache != COMPUTE_STOCK_INFINITE)
- {
- if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- suffix += " (" + LLTrans::getString("MarketplaceStock");
- }
- else
- {
- suffix += " (" + LLTrans::getString("MarketplaceMax");
- }
- if (m_stockCountCache == COMPUTE_STOCK_NOT_EVALUATED)
- {
- suffix += "=" + LLTrans::getString("MarketplaceUpdating") + ")";
- }
- else
- {
- suffix += "=" + llformat("%d", m_stockCountCache) + ")";
- }
- }
- // Add updating suffix
- if (updating)
- {
- suffix += " (" + LLTrans::getString("MarketplaceUpdating") + ")";
- }
- return LLInvFVBridge::getLabelSuffix() + suffix;
-}
-
-LLFontGL::StyleFlags LLMarketplaceFolderBridge::getLabelStyle() const
-{
- return (LLMarketplaceData::instance().getActivationState(getUUID()) ? LLFontGL::BOLD : LLFontGL::NORMAL);
-}
-
-
-
-
-// helper stuff
-bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, std::shared_ptr<LLMoveInv> move_inv)
-{
- LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData;
- LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID);
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-
- if(option == 0 && object)
- {
- if (cat_and_wear && cat_and_wear->mWear) // && !cat_and_wear->mFolderResponded)
- {
- LLInventoryObject::object_list_t inventory_objects;
- object->getInventoryContents(inventory_objects);
- int contents_count = inventory_objects.size();
- LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count, cat_and_wear->mFolderResponded,
- cat_and_wear->mReplace);
-
- gInventory.addObserver(inventoryObserver);
- }
-
- two_uuids_list_t::iterator move_it;
- for (move_it = move_inv->mMoveList.begin();
- move_it != move_inv->mMoveList.end();
- ++move_it)
- {
- object->moveInventory(move_it->first, move_it->second);
- }
-
- // update the UI.
- dialog_refresh_all();
- }
-
- if (move_inv->mCallback)
- {
- move_inv->mCallback(option, move_inv->mUserData, move_inv.get());
- }
-
- move_inv.reset(); //since notification will persist
- return false;
-}
-
-void drop_to_favorites_cb(const LLUUID& id, LLPointer<LLInventoryCallback> cb1, LLPointer<LLInventoryCallback> cb2)
-{
- cb1->fire(id);
- cb2->fire(id);
-}
-
-LLFolderBridge::LLFolderBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid)
- : LLInvFVBridge(inventory, root, uuid)
- , mCallingCards(false)
- , mWearables(false)
- , mIsLoading(false)
- , mShowDescendantsCount(false)
- , mCanDeleteFolderState(CDS_DONE)
- , mLastCheckedVersion(S32_MIN)
- , mInProgressVersion(S32_MIN)
- , mCanDelete(false)
- , mCanCut(false)
-{
-}
-
-LLFolderBridge::~LLFolderBridge()
-{
- gIdleCallbacks.deleteFunction(onCanDeleteIdle, this);
-}
-
-void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb)
-{
- // use callback to rearrange favorite landmarks after adding
- // to have new one placed before target (on which it was dropped). See EXT-4312.
- LLPointer<AddFavoriteLandmarkCallback> cb_fav = new AddFavoriteLandmarkCallback();
- LLInventoryPanel* panel = mInventoryPanel.get();
- LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL;
- LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL;
- if (view_model)
- {
- cb_fav.get()->setTargetLandmarkId(view_model->getUUID());
- }
-
- LLPointer <LLInventoryCallback> callback = cb_fav;
- if (cb)
- {
- callback = new LLBoostFuncInventoryCallback(boost::bind(drop_to_favorites_cb, _1, cb, cb_fav));
- }
-
- copy_inventory_item(
- gAgent.getID(),
- inv_item->getPermissions().getOwner(),
- inv_item->getUUID(),
- mUUID,
- std::string(),
- callback);
-}
-
-void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb)
-{
- if((inv_item->getInventoryType() == LLInventoryType::IT_TEXTURE) || (inv_item->getInventoryType() == LLInventoryType::IT_SNAPSHOT))
- {
- const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
- if(mUUID != my_outifts_id)
- {
- // Legacy: prior to thumbnails images in outfits were used for outfit gallery.
- LLNotificationsUtil::add("ThumbnailOutfitPhoto");
- }
- return;
- }
-
- // BAP - should skip if dup.
- if (move_is_into_current_outfit)
- {
- LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true);
- }
- else
- {
- LLPointer<LLInventoryCallback> cb = NULL;
- link_inventory_object(mUUID, LLConstPointer<LLInventoryObject>(inv_item), cb);
- }
-}
-
-void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb)
-{
- // make a folder in the My Outfits directory.
- const LLUUID dest_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
-
- // Note: creation will take time, so passing folder id to callback is slightly unreliable,
- // but so is collecting and passing descendants' ids
- inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb);
- gInventory.createNewCategory(dest_id,
- LLFolderType::FT_OUTFIT,
- inv_cat->getName(),
- func,
- inv_cat->getThumbnailUUID());
-}
-
-void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb)
-{
- LLInventoryModel::cat_array_t* categories;
- LLInventoryModel::item_array_t* items;
- getInventoryModel()->getDirectDescendentsOf(cat_source_id, categories, items);
-
- LLInventoryObject::const_object_list_t link_array;
-
-
- LLInventoryModel::item_array_t::iterator iter = items->begin();
- LLInventoryModel::item_array_t::iterator end = items->end();
- while (iter!=end)
- {
- const LLViewerInventoryItem* item = (*iter);
- // By this point everything is supposed to be filtered,
- // but there was a delay to create folder so something could have changed
- LLInventoryType::EType inv_type = item->getInventoryType();
- if ((inv_type == LLInventoryType::IT_WEARABLE) ||
- (inv_type == LLInventoryType::IT_GESTURE) ||
- (inv_type == LLInventoryType::IT_ATTACHMENT) ||
- (inv_type == LLInventoryType::IT_OBJECT) ||
- (inv_type == LLInventoryType::IT_SNAPSHOT) ||
- (inv_type == LLInventoryType::IT_TEXTURE))
- {
- link_array.push_back(LLConstPointer<LLInventoryObject>(item));
- }
- iter++;
- }
-
- if (!link_array.empty())
- {
- link_inventory_array(cat_dest_id, link_array, cb);
- }
-}
-
-// Callback for drop item if DAMA required...
-void LLFolderBridge::callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option == 0) // YES
- {
- std::string tooltip_msg;
- dragItemIntoFolder(inv_item, true, tooltip_msg, false);
- }
-}
-
-// Callback for drop category if DAMA required...
-void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option == 0) // YES
- {
- std::string tooltip_msg;
- dragCategoryIntoFolder(inv_category, true, tooltip_msg, false, false);
- }
-}
-
-// This is used both for testing whether an item can be dropped
-// into the folder, as well as performing the actual drop, depending
-// if drop == true.
-bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
- bool drop,
- std::string& tooltip_msg,
- bool user_confirm,
- LLPointer<LLInventoryCallback> cb)
-{
- LLInventoryModel* model = getInventoryModel();
-
- if (!model || !inv_item) return false;
- if (!isAgentInventory()) return false; // cannot drag into library
- if (!isAgentAvatarValid()) return false;
-
- LLInventoryPanel* destination_panel = mInventoryPanel.get();
- if (!destination_panel) return false;
-
- LLInventoryFilter* filter = getInventoryFilter();
- if (!filter) return false;
-
- const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
- const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
- const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
- const LLUUID from_folder_uuid = inv_item->getParentUUID();
-
- const bool move_is_into_current_outfit = (mUUID == current_outfit_id);
- const bool move_is_into_favorites = (mUUID == favorites_id);
- const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
- const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
- const bool move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id);
- const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
- const bool move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id);
-
- LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
- bool accept = false;
- U64 filter_types = filter->getFilterTypes();
- // We shouldn't allow to drop non recent items into recent tab (or some similar transactions)
- // while we are allowing to interact with regular filtered inventory
- bool use_filter = filter_types && (filter_types&LLInventoryFilter::FILTERTYPE_DATE || (filter_types&LLInventoryFilter::FILTERTYPE_OBJECT)==0);
- LLViewerObject* object = NULL;
- if(LLToolDragAndDrop::SOURCE_AGENT == source)
- {
- const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
-
- const bool move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
- const bool move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID());
-
- //--------------------------------------------------------------------------------
- // Determine if item can be moved.
- //
-
- bool is_movable = true;
-
- switch (inv_item->getActualType())
- {
- case LLAssetType::AT_CATEGORY:
- is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType());
- break;
- default:
- break;
- }
- // Can't explicitly drag things out of the COF.
- if (move_is_outof_current_outfit)
- {
- is_movable = false;
- }
- if (move_is_into_trash)
- {
- is_movable &= inv_item->getIsLinkType() || !get_is_item_worn(inv_item->getUUID());
- }
- if (is_movable)
- {
- // Don't allow creating duplicates in the Calling Card/Friends
- // subfolders, see bug EXT-1599. Check is item direct descendent
- // of target folder and forbid item's movement if it so.
- // Note: isItemDirectDescendentOfCategory checks if
- // passed category is in the Calling Card/Friends folder
- is_movable &= !LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(inv_item, getCategory());
- }
-
- //
- //--------------------------------------------------------------------------------
-
- //--------------------------------------------------------------------------------
- // Determine if item can be moved & dropped
- // Note: if user_confirm is false, we already went through those accept logic test and can skip them
-
- accept = true;
-
- if (user_confirm && !is_movable)
- {
- accept = false;
- }
- else if (user_confirm && (mUUID == inv_item->getParentUUID()) && !move_is_into_favorites)
- {
- accept = false;
- }
- else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit))
- {
- accept = can_move_to_outfit(inv_item, move_is_into_current_outfit);
- }
- else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks))
- {
- accept = can_move_to_landmarks(inv_item);
- }
- else if (user_confirm && move_is_into_marketplacelistings)
- {
- const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID);
- LLViewerInventoryCategory * dest_folder = getCategory();
- accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex());
- }
-
- // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility)
- if (user_confirm && accept)
- {
- LLViewerInventoryCategory * dest_folder = getCategory();
- accept = dest_folder->acceptItem(inv_item);
- }
-
- LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(false);
-
- // Check whether the item being dragged from active inventory panel
- // passes the filter of the destination panel.
- if (user_confirm && accept && active_panel && use_filter)
- {
- LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID());
- if (!fv_item) return false;
-
- accept = filter->check(fv_item->getViewModelItem());
- }
-
- if (accept && drop)
- {
- if (inv_item->getType() == LLAssetType::AT_GESTURE
- && LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
- {
- LLGestureMgr::instance().deactivateGesture(inv_item->getUUID());
- }
- // If an item is being dragged between windows, unselect everything in the active window
- // so that we don't follow the selection to its new location (which is very annoying).
- // RN: a better solution would be to deselect automatically when an item is moved
- // and then select any item that is dropped only in the panel that it is dropped in
- if (active_panel && (destination_panel != active_panel))
- {
- active_panel->unSelectAll();
- }
- // Dropping in or out of marketplace needs (sometimes) confirmation
- if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings))
- {
- if ((move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(inv_item->getUUID())
- || LLMarketplaceData::instance().isListedAndActive(inv_item->getUUID()))) ||
- (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID)))
- {
- LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item));
- return true;
- }
- if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings)
- {
- LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item));
- return true;
- }
- }
-
- //--------------------------------------------------------------------------------
- // Destination folder logic
- //
-
- // REORDER
- // (only reorder the item in Favorites folder)
- if ((mUUID == inv_item->getParentUUID()) && move_is_into_favorites)
- {
- LLFolderViewItem* itemp = destination_panel->getRootFolder()->getDraggingOverItem();
- if (itemp)
- {
- LLUUID srcItemId = inv_item->getUUID();
- LLUUID destItemId = static_cast<LLFolderViewModelItemInventory*>(itemp->getViewModelItem())->getUUID();
- LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId);
- }
- }
-
- // FAVORITES folder
- // (copy the item)
- else if (move_is_into_favorites)
- {
- dropToFavorites(inv_item, cb);
- }
- // CURRENT OUTFIT or OUTFIT folder
- // (link the item)
- else if (move_is_into_current_outfit || move_is_into_outfit)
- {
- dropToOutfit(inv_item, move_is_into_current_outfit, cb);
- }
- // MARKETPLACE LISTINGS folder
- // Move the item
- else if (move_is_into_marketplacelistings)
- {
- move_item_to_marketplacelistings(inv_item, mUUID);
- if (cb) cb->fire(inv_item->getUUID());
- }
- // NORMAL or TRASH folder
- // (move the item, restamp if into trash)
- else
- {
- // set up observer to select item once drag and drop from inbox is complete
- if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX)))
- {
- set_dad_inbox_object(inv_item->getUUID());
- }
-
- LLInvFVBridge::changeItemParent(
- model,
- (LLViewerInventoryItem*)inv_item,
- mUUID,
- move_is_into_trash);
- if (cb) cb->fire(inv_item->getUUID());
- }
-
- if (move_is_from_marketplacelistings)
- {
- // If we move from an active (listed) listing, checks that it's still valid, if not, unlist
- LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid);
- if (version_folder_id.notNull())
- {
- LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
- version_folder_id,
- [version_folder_id](bool result)
- {
- if (!result)
- {
- LLMarketplaceData::instance().activateListing(version_folder_id, false);
- }
- });
- }
- }
-
- //
- //--------------------------------------------------------------------------------
- }
- }
- else if (LLToolDragAndDrop::SOURCE_WORLD == source)
- {
- // Make sure the object exists. If we allowed dragging from
- // anonymous objects, it would be possible to bypass
- // permissions.
- object = gObjectList.findObject(inv_item->getParentUUID());
- if (!object)
- {
- LL_INFOS() << "Object not found for drop." << LL_ENDL;
- return false;
- }
-
- // coming from a task. Need to figure out if the person can
- // move/copy this item.
- LLPermissions perm(inv_item->getPermissions());
- bool is_move = false;
- if ((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
- && perm.allowTransferTo(gAgent.getID())))
- // || gAgent.isGodlike())
- {
- accept = true;
- }
- else if(object->permYouOwner())
- {
- // If the object cannot be copied, but the object the
- // inventory is owned by the agent, then the item can be
- // moved from the task to agent inventory.
- is_move = true;
- accept = true;
- }
-
- // Don't allow placing an original item into Current Outfit or an outfit folder
- // because they must contain only links to wearable items.
- // *TODO: Probably we should create a link to an item if it was dragged to outfit or COF.
- if (move_is_into_current_outfit || move_is_into_outfit)
- {
- accept = false;
- }
- // Don't allow to move a single item to Favorites or Landmarks
- // if it is not a landmark or a link to a landmark.
- else if ((move_is_into_favorites || move_is_into_landmarks)
- && !can_move_to_landmarks(inv_item))
- {
- accept = false;
- }
- else if (move_is_into_marketplacelistings)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
- accept = false;
- }
-
- // Check whether the item being dragged from in world
- // passes the filter of the destination panel.
- if (accept && use_filter)
- {
- accept = filter->check(inv_item);
- }
-
- if (accept && drop)
- {
- LLUUID item_id = inv_item->getUUID();
- std::shared_ptr<LLMoveInv> move_inv (new LLMoveInv());
- move_inv->mObjectID = inv_item->getParentUUID();
- two_uuids_t item_pair(mUUID, item_id);
- move_inv->mMoveList.push_back(item_pair);
- if (cb)
- {
- move_inv->mCallback = [item_id, cb](S32, void*, const LLMoveInv* move_inv) mutable
- { cb->fire(item_id); };
- }
- move_inv->mUserData = NULL;
- if(is_move)
- {
- warn_move_inventory(object, move_inv);
- }
- else
- {
- // store dad inventory item to select added one later. See EXT-4347
- set_dad_inventory_item(inv_item, mUUID);
-
- LLNotification::Params params("MoveInventoryFromObject");
- params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
- LLNotifications::instance().forceResponse(params, 0);
- }
- }
- }
- else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
- {
- if (move_is_into_marketplacelistings)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
- accept = false;
- }
- else if ((inv_item->getActualType() == LLAssetType::AT_SETTINGS) && !LLEnvironment::instance().isInventoryEnabled())
- {
- tooltip_msg = LLTrans::getString("NoEnvironmentSettings");
- accept = false;
- }
- else
- {
- // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder
- // because they must contain only links to wearable items.
- accept = !(move_is_into_current_outfit || move_is_into_outfit);
- }
-
- // Check whether the item being dragged from notecard
- // passes the filter of the destination panel.
- if (accept && use_filter)
- {
- accept = filter->check(inv_item);
- }
-
- if (accept && drop)
- {
- copy_inventory_from_notecard(mUUID, // Drop to the chosen destination folder
- LLToolDragAndDrop::getInstance()->getObjectID(),
- LLToolDragAndDrop::getInstance()->getSourceID(),
- inv_item);
- }
- }
- else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
- if(item && item->isFinished())
- {
- accept = true;
-
- if (move_is_into_marketplacelistings)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
- accept = false;
- }
- else if (move_is_into_current_outfit || move_is_into_outfit)
- {
- accept = can_move_to_outfit(inv_item, move_is_into_current_outfit);
- }
- // Don't allow to move a single item to Favorites or Landmarks
- // if it is not a landmark or a link to a landmark.
- else if (move_is_into_favorites || move_is_into_landmarks)
- {
- accept = can_move_to_landmarks(inv_item);
- }
-
- LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(false);
-
- // Check whether the item being dragged from the library
- // passes the filter of the destination panel.
- if (accept && active_panel && use_filter)
- {
- LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID());
- if (!fv_item) return false;
-
- accept = filter->check(fv_item->getViewModelItem());
- }
-
- if (accept && drop)
- {
- // FAVORITES folder
- // (copy the item)
- if (move_is_into_favorites)
- {
- dropToFavorites(inv_item, cb);
- }
- // CURRENT OUTFIT or OUTFIT folder
- // (link the item)
- else if (move_is_into_current_outfit || move_is_into_outfit)
- {
- dropToOutfit(inv_item, move_is_into_current_outfit, cb);
- }
- else
- {
- copy_inventory_item(
- gAgent.getID(),
- inv_item->getPermissions().getOwner(),
- inv_item->getUUID(),
- mUUID,
- std::string(),
- cb);
- }
- }
- }
- }
- else
- {
- LL_WARNS() << "unhandled drag source" << LL_ENDL;
- }
- return accept;
-}
-
-// static
-bool check_category(LLInventoryModel* model,
- const LLUUID& cat_id,
- LLInventoryPanel* active_panel,
- LLInventoryFilter* filter)
-{
- if (!model || !active_panel || !filter)
- return false;
-
- if (!filter->checkFolder(cat_id))
- {
- return false;
- }
-
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- model->collectDescendents(cat_id, descendent_categories, descendent_items, true);
-
- S32 num_descendent_categories = descendent_categories.size();
- S32 num_descendent_items = descendent_items.size();
-
- if (num_descendent_categories + num_descendent_items == 0)
- {
- // Empty folder should be checked as any other folder view item.
- // If we are filtering by date the folder should not pass because
- // it doesn't have its own creation date. See LLInvFVBridge::getCreationDate().
- return check_item(cat_id, active_panel, filter);
- }
-
- for (S32 i = 0; i < num_descendent_categories; ++i)
- {
- LLInventoryCategory* category = descendent_categories[i];
- if(!check_category(model, category->getUUID(), active_panel, filter))
- {
- return false;
- }
- }
-
- for (S32 i = 0; i < num_descendent_items; ++i)
- {
- LLViewerInventoryItem* item = descendent_items[i];
- if(!check_item(item->getUUID(), active_panel, filter))
- {
- return false;
- }
- }
-
- return true;
-}
-
-// static
-bool check_item(const LLUUID& item_id,
- LLInventoryPanel* active_panel,
- LLInventoryFilter* filter)
-{
- if (!active_panel || !filter) return false;
-
- LLFolderViewItem* fv_item = active_panel->getItemByID(item_id);
- if (!fv_item) return false;
-
- return filter->check(fv_item->getViewModelItem());
-}
-
-// +=================================================+
-// | LLTextureBridge |
-// +=================================================+
-
-LLUIImagePtr LLTextureBridge::getIcon() const
-{
- return LLInventoryIcon::getIcon(LLAssetType::AT_TEXTURE, mInvType);
-}
-
-void LLTextureBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-bool LLTextureBridge::canSaveTexture(void)
-{
- const LLInventoryModel* model = getInventoryModel();
- if(!model)
- {
- return false;
- }
-
- const LLViewerInventoryItem *item = model->getItem(mUUID);
- if (item)
- {
- return item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED);
- }
- return false;
-}
-
-void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLTextureBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
-
- addOpenRightClickMenuOption(items);
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- items.push_back(std::string("Texture Separator"));
-
- if ((flags & ITEM_IN_MULTI_SELECTION) != 0)
- {
- items.push_back(std::string("Save Selected As"));
- }
- else
- {
- items.push_back(std::string("Save As"));
- if (!canSaveTexture())
- {
- disabled_items.push_back(std::string("Save As"));
- }
- }
-
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-// virtual
-void LLTextureBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("save_as" == action)
- {
- LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUID);
- if (preview_texture)
- {
- preview_texture->openToSave();
- preview_texture->saveAs();
- }
- }
- else if ("save_selected_as" == action)
- {
- openItem();
- if (canSaveTexture())
- {
- LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUID);
- if (preview_texture)
- {
- preview_texture->saveMultipleToFile(mFileName);
- }
- }
- else
- {
- LL_WARNS() << "You don't have permission to save " << getName() << " to disk." << LL_ENDL;
- }
-
- }
- else LLItemBridge::performAction(model, action);
-}
-
-// +=================================================+
-// | LLSoundBridge |
-// +=================================================+
-
-void LLSoundBridge::openItem()
-{
- const LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-void LLSoundBridge::openSoundPreview(void* which)
-{
- LLSoundBridge *me = (LLSoundBridge *)which;
- LLFloaterReg::showInstance("preview_sound", LLSD(me->mUUID), TAKE_FOCUS_YES);
-}
-
-void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLSoundBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- if (isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
- items.push_back(std::string("Sound Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
-
- items.push_back(std::string("Sound Separator"));
- items.push_back(std::string("Sound Play"));
- }
-
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-void LLSoundBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("sound_play" == action)
- {
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- send_sound_trigger(item->getAssetUUID(), SOUND_GAIN);
- }
- }
- else if ("open" == action)
- {
- openSoundPreview((void*)this);
- }
- else LLItemBridge::performAction(model, action);
-}
-
-// +=================================================+
-// | LLLandmarkBridge |
-// +=================================================+
-
-LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid,
- U32 flags/* = 0x00*/) :
- LLItemBridge(inventory, root, uuid)
-{
- mVisited = false;
- if (flags & LLInventoryItemFlags::II_FLAGS_LANDMARK_VISITED)
- {
- mVisited = true;
- }
-}
-
-LLUIImagePtr LLLandmarkBridge::getIcon() const
-{
- return LLInventoryIcon::getIcon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited, false);
-}
-
-void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- LL_DEBUGS() << "LLLandmarkBridge::buildContextMenu()" << LL_ENDL;
- if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
- items.push_back(std::string("Landmark Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
-
- items.push_back(std::string("Landmark Separator"));
- items.push_back(std::string("url_copy"));
- items.push_back(std::string("About Landmark"));
- items.push_back(std::string("show_on_map"));
- }
-
- // Disable "About Landmark" menu item for
- // multiple landmarks selected. Only one landmark
- // info panel can be shown at a time.
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("url_copy"));
- disabled_items.push_back(std::string("About Landmark"));
- }
-
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-// Convenience function for the two functions below.
-void teleport_via_landmark(const LLUUID& asset_id)
-{
- gAgent.teleportViaLandmark( asset_id );
-
- // we now automatically track the landmark you're teleporting to
- // because you'll probably arrive at a telehub instead
- LLFloaterWorldMap* floater_world_map = LLFloaterWorldMap::getInstance();
- if( floater_world_map )
- {
- floater_world_map->trackLandmark( asset_id );
- }
-}
-
-// virtual
-void LLLandmarkBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("teleport" == action)
- {
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- teleport_via_landmark(item->getAssetUUID());
- }
- }
- else if ("about" == action)
- {
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- LLSD key;
- key["type"] = "landmark";
- key["id"] = item->getUUID();
-
- LLFloaterSidePanelContainer::showPanel("places", key);
- }
- }
- else
- {
- LLItemBridge::performAction(model, action);
- }
-}
-
-static bool open_landmark_callback(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-
- LLUUID asset_id = notification["payload"]["asset_id"].asUUID();
- if (option == 0)
- {
- teleport_via_landmark(asset_id);
- }
-
- return false;
-}
-static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFromLandmark", open_landmark_callback);
-
-
-void LLLandmarkBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-
-// +=================================================+
-// | LLCallingCardObserver |
-// +=================================================+
-class LLCallingCardObserver : public LLFriendObserver
-{
-public:
- LLCallingCardObserver(LLCallingCardBridge* bridge) : mBridgep(bridge) {}
- virtual ~LLCallingCardObserver() { mBridgep = NULL; }
- virtual void changed(U32 mask)
- {
- if (mask & LLFriendObserver::ONLINE)
- {
- mBridgep->refreshFolderViewItem();
- mBridgep->checkSearchBySuffixChanges();
- }
- }
-protected:
- LLCallingCardBridge* mBridgep;
-};
-
-// +=================================================+
-// | LLCallingCardBridge |
-// +=================================================+
-
-LLCallingCardBridge::LLCallingCardBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid ) :
- LLItemBridge(inventory, root, uuid)
-{
- mObserver = new LLCallingCardObserver(this);
- mCreatorUUID = getItem()->getCreatorUUID();
- LLAvatarTracker::instance().addParticularFriendObserver(mCreatorUUID, mObserver);
-}
-
-LLCallingCardBridge::~LLCallingCardBridge()
-{
- LLAvatarTracker::instance().removeParticularFriendObserver(mCreatorUUID, mObserver);
-
- delete mObserver;
-}
-
-void LLCallingCardBridge::refreshFolderViewItem()
-{
- LLInventoryPanel* panel = mInventoryPanel.get();
- LLFolderViewItem* itemp = panel ? panel->getItemByID(mUUID) : NULL;
- if (itemp)
- {
- itemp->refresh();
- }
-}
-
-void LLCallingCardBridge::checkSearchBySuffixChanges()
-{
- if (!mDisplayName.empty())
- {
- // changes in mDisplayName are processed by rename function and here it will be always same
- // suffixes are also of fixed length, and we are processing change of one at a time,
- // so it should be safe to use length (note: mSearchableName is capitalized)
- S32 old_length = mSearchableName.length();
- S32 new_length = mDisplayName.length() + getLabelSuffix().length();
- if (old_length == new_length)
- {
- return;
- }
- mSearchableName.assign(mDisplayName);
- mSearchableName.append(getLabelSuffix());
- LLStringUtil::toUpper(mSearchableName);
- if (new_length<old_length)
- {
- LLInventoryFilter* filter = getInventoryFilter();
- if (filter && mPassedFilter && mSearchableName.find(filter->getFilterSubString()) == std::string::npos)
- {
- // string no longer contains substring
- // we either have to update all parents manually or restart filter.
- // dirtyFilter will not work here due to obsolete descendants' generations
- getInventoryFilter()->setModified(LLFolderViewFilter::FILTER_MORE_RESTRICTIVE);
- }
- }
- else
- {
- if (getInventoryFilter())
- {
- // mSearchableName became longer, we gained additional suffix and need to repeat filter check.
- dirtyFilter();
- }
- }
- }
-}
-
-// virtual
-void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("begin_im" == action)
- {
- LLViewerInventoryItem *item = getItem();
- if (item && (item->getCreatorUUID() != gAgent.getID()) &&
- (!item->getCreatorUUID().isNull()))
- {
- std::string callingcard_name = LLCacheName::getDefaultName();
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name))
- {
- callingcard_name = av_name.getCompleteName();
- }
- LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID());
- if (session_id != LLUUID::null)
- {
- LLFloaterIMContainer::getInstance()->showConversation(session_id);
- }
- }
- }
- else if ("lure" == action)
- {
- LLViewerInventoryItem *item = getItem();
- if (item && (item->getCreatorUUID() != gAgent.getID()) &&
- (!item->getCreatorUUID().isNull()))
- {
- LLAvatarActions::offerTeleport(item->getCreatorUUID());
- }
- }
- else if ("request_lure" == action)
- {
- LLViewerInventoryItem *item = getItem();
- if (item && (item->getCreatorUUID() != gAgent.getID()) &&
- (!item->getCreatorUUID().isNull()))
- {
- LLAvatarActions::teleportRequest(item->getCreatorUUID());
- }
- }
-
- else LLItemBridge::performAction(model, action);
-}
-
-LLUIImagePtr LLCallingCardBridge::getIcon() const
-{
- bool online = false;
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
- }
- return LLInventoryIcon::getIcon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online, false);
-}
-
-std::string LLCallingCardBridge::getLabelSuffix() const
-{
- LLViewerInventoryItem* item = getItem();
- if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
- {
- return LLItemBridge::getLabelSuffix() + " online";
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-void LLCallingCardBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if(item && !item->getCreatorUUID().isNull())
- {
- LLAvatarActions::showProfile(item->getCreatorUUID());
- }
-*/
-}
-
-void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLCallingCardBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Open"));
- }
- addOpenRightClickMenuOption(items);
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- LLInventoryItem* item = getItem();
- bool good_card = (item
- && (LLUUID::null != item->getCreatorUUID())
- && (item->getCreatorUUID() != gAgent.getID()));
- bool user_online = false;
- if (item)
- {
- user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()));
- }
- items.push_back(std::string("Send Instant Message Separator"));
- items.push_back(std::string("Send Instant Message"));
- items.push_back(std::string("Offer Teleport..."));
- items.push_back(std::string("Request Teleport..."));
- items.push_back(std::string("Conference Chat"));
-
- if (!good_card)
- {
- disabled_items.push_back(std::string("Send Instant Message"));
- }
- if (!good_card || !user_online)
- {
- disabled_items.push_back(std::string("Offer Teleport..."));
- disabled_items.push_back(std::string("Request Teleport..."));
- disabled_items.push_back(std::string("Conference Chat"));
- }
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-bool LLCallingCardBridge::dragOrDrop(MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- std::string& tooltip_msg)
-{
- LLViewerInventoryItem* item = getItem();
- bool rv = false;
- if(item)
- {
- // check the type
- switch(cargo_type)
- {
- case DAD_TEXTURE:
- case DAD_SOUND:
- case DAD_LANDMARK:
- case DAD_SCRIPT:
- case DAD_CLOTHING:
- case DAD_OBJECT:
- case DAD_NOTECARD:
- case DAD_BODYPART:
- case DAD_ANIMATION:
- case DAD_GESTURE:
- case DAD_MESH:
- case DAD_SETTINGS:
- case DAD_MATERIAL:
- {
- LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
- const LLPermissions& perm = inv_item->getPermissions();
- if(gInventory.getItem(inv_item->getUUID())
- && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
- {
- rv = true;
- if(drop)
- {
- LLGiveInventory::doGiveInventoryItem(item->getCreatorUUID(),
- (LLInventoryItem*)cargo_data);
- }
- }
- else
- {
- // It's not in the user's inventory (it's probably in
- // an object's contents), so disallow dragging it here.
- // You can't give something you don't yet have.
- rv = false;
- }
- break;
- }
- case DAD_CATEGORY:
- {
- LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data;
- if( gInventory.getCategory( inv_cat->getUUID() ) )
- {
- rv = true;
- if(drop)
- {
- LLGiveInventory::doGiveInventoryCategory(
- item->getCreatorUUID(),
- inv_cat);
- }
- }
- else
- {
- // It's not in the user's inventory (it's probably in
- // an object's contents), so disallow dragging it here.
- // You can't give something you don't yet have.
- rv = false;
- }
- break;
- }
- default:
- break;
- }
- }
- return rv;
-}
-
-// +=================================================+
-// | LLNotecardBridge |
-// +=================================================+
-
-void LLNotecardBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-void LLNotecardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLNotecardBridge::buildContextMenu()" << LL_ENDL;
-
- if (isMarketplaceListingsFolder())
- {
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- hide_context_entries(menu, items, disabled_items);
- }
- else
- {
- LLItemBridge::buildContextMenu(menu, flags);
- }
-}
-
-// +=================================================+
-// | LLGestureBridge |
-// +=================================================+
-
-LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const
-{
- if( LLGestureMgr::instance().isGestureActive(mUUID) )
- {
- return LLFontGL::BOLD;
- }
- else
- {
- return LLFontGL::NORMAL;
- }
-}
-
-std::string LLGestureBridge::getLabelSuffix() const
-{
- if( LLGestureMgr::instance().isGestureActive(mUUID) )
- {
- LLStringUtil::format_map_t args;
- args["[GESLABEL]"] = LLItemBridge::getLabelSuffix();
- return LLTrans::getString("ActiveGesture", args);
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-// virtual
-void LLGestureBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if (isAddAction(action))
- {
- LLGestureMgr::instance().activateGesture(mUUID);
-
- LLViewerInventoryItem* item = gInventory.getItem(mUUID);
- if (!item) return;
-
- // Since we just changed the suffix to indicate (active)
- // the server doesn't need to know, just the viewer.
- gInventory.updateItem(item);
- gInventory.notifyObservers();
- }
- else if ("deactivate" == action || isRemoveAction(action))
- {
- LLGestureMgr::instance().deactivateGesture(mUUID);
-
- LLViewerInventoryItem* item = gInventory.getItem(mUUID);
- if (!item) return;
-
- // Since we just changed the suffix to indicate (active)
- // the server doesn't need to know, just the viewer.
- gInventory.updateItem(item);
- gInventory.notifyObservers();
- }
- else if("play" == action)
- {
- if(!LLGestureMgr::instance().isGestureActive(mUUID))
- {
- // we need to inform server about gesture activating to be consistent with LLPreviewGesture and LLGestureComboList.
- bool inform_server = true;
- bool deactivate_similar = false;
- LLGestureMgr::instance().setGestureLoadedCallback(mUUID, boost::bind(&LLGestureBridge::playGesture, mUUID));
- LLViewerInventoryItem* item = gInventory.getItem(mUUID);
- llassert(item);
- if (item)
- {
- LLGestureMgr::instance().activateGestureWithAsset(mUUID, item->getAssetUUID(), inform_server, deactivate_similar);
- }
- }
- else
- {
- playGesture(mUUID);
- }
- }
- else LLItemBridge::performAction(model, action);
-}
-
-void LLGestureBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
- preview->setFocus(true);
- }
-*/
-}
-
-bool LLGestureBridge::removeItem()
-{
- // Grab class information locally since *this may be deleted
- // within this function. Not a great pattern...
- const LLInventoryModel* model = getInventoryModel();
- if(!model)
- {
- return false;
- }
- const LLUUID item_id = mUUID;
-
- // This will also force close the preview window, if it exists.
- // This may actually delete *this, if mUUID is in the COF.
- LLGestureMgr::instance().deactivateGesture(item_id);
-
- // If deactivateGesture deleted *this, then return out immediately.
- if (!model->getObject(item_id))
- {
- return true;
- }
-
- return LLItemBridge::removeItem();
-}
-
-void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLGestureBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
-
- addOpenRightClickMenuOption(items);
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- items.push_back(std::string("Gesture Separator"));
- if (LLGestureMgr::instance().isGestureActive(getUUID()))
- {
- items.push_back(std::string("Deactivate"));
- }
- else
- {
- items.push_back(std::string("Activate"));
- }
- items.push_back(std::string("PlayGesture"));
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-// static
-void LLGestureBridge::playGesture(const LLUUID& item_id)
-{
- if (LLGestureMgr::instance().isGesturePlaying(item_id))
- {
- LLGestureMgr::instance().stopGesture(item_id);
- }
- else
- {
- LLGestureMgr::instance().playGesture(item_id);
- }
-}
-
-
-// +=================================================+
-// | LLAnimationBridge |
-// +=================================================+
-
-void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- LL_DEBUGS() << "LLAnimationBridge::buildContextMenu()" << LL_ENDL;
- if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
- items.push_back(std::string("Animation Open"));
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
- }
-
- items.push_back(std::string("Animation Separator"));
- items.push_back(std::string("Animation Play"));
- items.push_back(std::string("Animation Audition"));
- }
-
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-// virtual
-void LLAnimationBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ((action == "playworld") || (action == "playlocal"))
- {
- if (getItem())
- {
- LLSD::String activate = "NONE";
- if ("playworld" == action) activate = "Inworld";
- if ("playlocal" == action) activate = "Locally";
-
- LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID));
- if (preview)
- {
- preview->play(activate);
- }
- }
- }
- else
- {
- LLItemBridge::performAction(model, action);
- }
-}
-
-void LLAnimationBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-/*
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
- }
-*/
-}
-
-// +=================================================+
-// | LLObjectBridge |
-// +=================================================+
-
-// static
-LLUUID LLObjectBridge::sContextMenuItemID;
-
-LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid,
- LLInventoryType::EType type,
- U32 flags) :
- LLItemBridge(inventory, root, uuid)
-{
- mAttachPt = (flags & 0xff); // low bye of inventory flags
- mIsMultiObject = is_flag_set(flags, LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS);
- mInvType = type;
-}
-
-LLUIImagePtr LLObjectBridge::getIcon() const
-{
- return LLInventoryIcon::getIcon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject);
-}
-
-LLInventoryObject* LLObjectBridge::getObject() const
-{
- LLInventoryObject* object = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- object = (LLInventoryObject*)model->getObject(mUUID);
- }
- return object;
-}
-
-LLViewerInventoryItem* LLObjectBridge::getItem() const
-{
- LLInventoryModel* model = getInventoryModel();
- if (model)
- {
- return model->getItem(mUUID);
- }
- return NULL;
-}
-
-LLViewerInventoryCategory* LLObjectBridge::getCategory() const
-{
- LLInventoryModel* model = getInventoryModel();
- if (model)
- {
- return model->getCategory(mUUID);
- }
- return NULL;
-}
-
-// virtual
-void LLObjectBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if (isAddAction(action))
- {
- LLUUID object_id = mUUID;
- LLViewerInventoryItem* item;
- item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
- if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID()))
- {
- rez_attachment(item, NULL, true); // Replace if "Wear"ing.
- }
- else if(item && item->isFinished())
- {
- // must be in library. copy it to our inventory and put it on.
- LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0));
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- LLUUID::null,
- std::string(),
- cb);
- }
- gFocusMgr.setKeyboardFocus(NULL);
- }
- else if ("wear_add" == action)
- {
- LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding.
- }
- else if ("touch" == action)
- {
- handle_attachment_touch(mUUID);
- }
- else if ("edit" == action)
- {
- handle_attachment_edit(mUUID);
- }
- else if (isRemoveAction(action))
- {
- LLAppearanceMgr::instance().removeItemFromAvatar(mUUID);
- }
- else LLItemBridge::performAction(model, action);
-}
-
-void LLObjectBridge::openItem()
-{
- // object double-click action is to wear/unwear object
- performAction(getInventoryModel(),
- get_is_item_worn(mUUID) ? "detach" : "attach");
-}
-
-std::string LLObjectBridge::getLabelSuffix() const
-{
- if (get_is_item_worn(mUUID))
- {
- if (!isAgentAvatarValid()) // Error condition, can't figure out attach point
- {
- return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn");
- }
- std::string attachment_point_name;
- if (gAgentAvatarp->getAttachedPointName(mUUID, attachment_point_name))
- {
- LLStringUtil::format_map_t args;
- args["[ATTACHMENT_POINT]"] = LLTrans::getString(attachment_point_name);
-
- return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args);
- }
- else
- {
- LLStringUtil::format_map_t args;
- args["[ATTACHMENT_ERROR]"] = LLTrans::getString(attachment_point_name);
- return LLItemBridge::getLabelSuffix() + LLTrans::getString("AttachmentErrorMessage", args);
- }
- }
- return LLItemBridge::getLabelSuffix();
-}
-
-void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace)
-{
- const LLUUID& item_id = item->getLinkedUUID();
-
- // Check for duplicate request.
- if (isAgentAvatarValid() &&
- gAgentAvatarp->isWearingAttachment(item_id))
- {
- LL_WARNS() << "ATT duplicate attachment request, ignoring" << LL_ENDL;
- return;
- }
-
- S32 attach_pt = 0;
- if (isAgentAvatarValid() && attachment)
- {
- for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
- iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter)
- {
- if (iter->second == attachment)
- {
- attach_pt = iter->first;
- break;
- }
- }
- }
-
- LLSD payload;
- payload["item_id"] = item_id; // Wear the base object in case this is a link.
- payload["attachment_point"] = attach_pt;
- payload["is_add"] = !replace;
-
- if (replace &&
- (attachment && attachment->getNumObjects() > 0))
- {
- LLNotificationsUtil::add("ReplaceAttachment", LLSD(), payload, confirm_attachment_rez);
- }
- else
- {
- LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/);
- }
-}
-
-bool confirm_attachment_rez(const LLSD& notification, const LLSD& response)
-{
- if (!gAgentAvatarp->canAttachMoreObjects())
- {
- LLSD args;
- args["MAX_ATTACHMENTS"] = llformat("%d", gAgentAvatarp->getMaxAttachments());
- LLNotificationsUtil::add("MaxAttachmentsOnOutfit", args);
- return false;
- }
-
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option == 0/*YES*/)
- {
- LLUUID item_id = notification["payload"]["item_id"].asUUID();
- LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
-
- if (itemp)
- {
- // Queue up attachments to be sent in next idle tick, this way the
- // attachments are batched up all into one message versus each attachment
- // being sent in its own separate attachments message.
- U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
- bool is_add = notification["payload"]["is_add"].asBoolean();
-
- LL_DEBUGS("Avatar") << "ATT calling addAttachmentRequest " << (itemp ? itemp->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
- LLAttachmentsMgr::instance().addAttachmentRequest(item_id, attachment_pt, is_add);
- }
- }
- return false;
-}
-static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_attachment_rez);
-
-void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
-
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- LLObjectBridge::sContextMenuItemID = mUUID;
-
- LLInventoryItem *item = getItem();
- if(item)
- {
- if (!isAgentAvatarValid()) return;
-
- if( get_is_item_worn( mUUID ) )
- {
- items.push_back(std::string("Wearable And Object Separator"));
-
- items.push_back(std::string("Attachment Touch"));
- if ( ((flags & FIRST_SELECTED_ITEM) == 0) || !enable_attachment_touch(mUUID) )
- {
- disabled_items.push_back(std::string("Attachment Touch"));
- }
-
- items.push_back(std::string("Wearable Edit"));
- if ( ((flags & FIRST_SELECTED_ITEM) == 0) || !get_is_item_editable(mUUID) )
- {
- disabled_items.push_back(std::string("Wearable Edit"));
- }
-
- items.push_back(std::string("Detach From Yourself"));
- }
- else if (!isItemInTrash() && !isLinkedObjectInTrash() && !isLinkedObjectMissing() && !isCOFFolder())
- {
- items.push_back(std::string("Wearable And Object Separator"));
- items.push_back(std::string("Wearable And Object Wear"));
- items.push_back(std::string("Wearable Add"));
- items.push_back(std::string("Attach To"));
- items.push_back(std::string("Attach To HUD"));
- // commented out for DEV-32347
- //items.push_back(std::string("Restore to Last Position"));
-
- if (!gAgentAvatarp->canAttachMoreObjects())
- {
- disabled_items.push_back(std::string("Wearable And Object Wear"));
- disabled_items.push_back(std::string("Wearable Add"));
- disabled_items.push_back(std::string("Attach To"));
- disabled_items.push_back(std::string("Attach To HUD"));
- }
- LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", true);
- LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", true);
- if (attach_menu
- && (attach_menu->getChildCount() == 0)
- && attach_hud_menu
- && (attach_hud_menu->getChildCount() == 0)
- && isAgentAvatarValid())
- {
- for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
- iter != gAgentAvatarp->mAttachmentPoints.end(); )
- {
- LLVOAvatar::attachment_map_t::iterator curiter = iter++;
- LLViewerJointAttachment* attachment = curiter->second;
- LLMenuItemCallGL::Params p;
- std::string submenu_name = attachment->getName();
- if (LLTrans::getString(submenu_name) != "")
- {
- p.name = (" ")+LLTrans::getString(submenu_name)+" ";
- }
- else
- {
- p.name = submenu_name;
- }
- LLSD cbparams;
- cbparams["index"] = curiter->first;
- cbparams["label"] = p.name;
- p.on_click.function_name = "Inventory.AttachObject";
- p.on_click.parameter = LLSD(attachment->getName());
- p.on_enable.function_name = "Attachment.Label";
- p.on_enable.parameter = cbparams;
- LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu;
- LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
- items.push_back(p.name);
- }
- }
- }
- }
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-bool LLObjectBridge::renameItem(const std::string& new_name)
-{
- if(!isItemRenameable())
- return false;
- LLPreview::dirty(mUUID);
- LLInventoryModel* model = getInventoryModel();
- if(!model)
- return false;
- LLViewerInventoryItem* item = getItem();
- if(item && (item->getName() != new_name))
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->rename(new_name);
- new_item->updateServer(false);
- model->updateItem(new_item);
- model->notifyObservers();
- buildDisplayName();
-
- if (isAgentAvatarValid())
- {
- LLViewerObject* obj = gAgentAvatarp->getWornAttachment( item->getUUID() );
- if(obj)
- {
- LLSelectMgr::getInstance()->deselectAll();
- LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, false );
- LLSelectMgr::getInstance()->selectionSetObjectName( new_name );
- LLSelectMgr::getInstance()->deselectAll();
- }
- }
- }
- // return false because we either notified observers (& therefore
- // rebuilt) or we didn't update.
- return false;
-}
-
-// +=================================================+
-// | LLLSLTextBridge |
-// +=================================================+
-
-void LLLSLTextBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
-
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-// +=================================================+
-// | LLWearableBridge |
-// +=================================================+
-
-LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid,
- LLAssetType::EType asset_type,
- LLInventoryType::EType inv_type,
- LLWearableType::EType wearable_type) :
- LLItemBridge(inventory, root, uuid),
- mAssetType( asset_type ),
- mWearableType(wearable_type)
-{
- mInvType = inv_type;
-}
-
-bool LLWearableBridge::renameItem(const std::string& new_name)
-{
- if (get_is_item_worn(mUUID))
- {
- gAgentWearables.setWearableName( mUUID, new_name );
- }
- return LLItemBridge::renameItem(new_name);
-}
-
-std::string LLWearableBridge::getLabelSuffix() const
-{
- if (get_is_item_worn(mUUID))
- {
- // e.g. "(worn)"
- return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn");
- }
- else
- {
- return LLItemBridge::getLabelSuffix();
- }
-}
-
-LLUIImagePtr LLWearableBridge::getIcon() const
-{
- return LLInventoryIcon::getIcon(mAssetType, mInvType, mWearableType, false);
-}
-
-// virtual
-void LLWearableBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if (isAddAction(action))
- {
- wearOnAvatar();
- }
- else if ("wear_add" == action)
- {
- wearAddOnAvatar();
- }
- else if ("edit" == action)
- {
- editOnAvatar();
- return;
- }
- else if (isRemoveAction(action))
- {
- removeFromAvatar();
- return;
- }
- else LLItemBridge::performAction(model, action);
-}
-
-void LLWearableBridge::openItem()
-{
- performAction(getInventoryModel(),
- get_is_item_worn(mUUID) ? "take_off" : "wear");
-}
-
-void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLWearableBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else if (isMarketplaceListingsFolder())
- {
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- }
- else
- { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere
- bool can_open = ((flags & SUPPRESS_OPEN_ITEM) != SUPPRESS_OPEN_ITEM);
-
- // If we have clothing, don't add "Open" as it's the same action as "Wear" SL-18976
- LLViewerInventoryItem* item = getItem();
- if (can_open && item)
- {
- can_open = (item->getType() != LLAssetType::AT_CLOTHING) &&
- (item->getType() != LLAssetType::AT_BODYPART);
- }
- if (isLinkedObjectMissing())
- {
- can_open = false;
- }
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
-
- if (can_open)
- {
- addOpenRightClickMenuOption(items);
- }
- else
- {
- disabled_items.push_back(std::string("Open"));
- disabled_items.push_back(std::string("Open Original"));
- }
-
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- items.push_back(std::string("Wearable And Object Separator"));
- items.push_back(std::string("Wearable Edit"));
-
- if (((flags & FIRST_SELECTED_ITEM) == 0) || (item && !gAgentWearables.isWearableModifiable(item->getUUID())))
- {
- disabled_items.push_back(std::string("Wearable Edit"));
- }
- // Don't allow items to be worn if their baseobj is in the trash.
- if (isLinkedObjectInTrash() || isLinkedObjectMissing() || isCOFFolder())
- {
- disabled_items.push_back(std::string("Wearable And Object Wear"));
- disabled_items.push_back(std::string("Wearable Add"));
- disabled_items.push_back(std::string("Wearable Edit"));
- }
-
- // Disable wear and take off based on whether the item is worn.
- if(item)
- {
- switch (item->getType())
- {
- case LLAssetType::AT_CLOTHING:
- items.push_back(std::string("Take Off"));
- // Fallthrough since clothing and bodypart share wear options
- case LLAssetType::AT_BODYPART:
- if (get_is_item_worn(item->getUUID()))
- {
- disabled_items.push_back(std::string("Wearable And Object Wear"));
- disabled_items.push_back(std::string("Wearable Add"));
- }
- else
- {
- items.push_back(std::string("Wearable And Object Wear"));
- disabled_items.push_back(std::string("Take Off"));
- disabled_items.push_back(std::string("Wearable Edit"));
- }
-
- if (LLWearableType::getInstance()->getAllowMultiwear(mWearableType))
- {
- items.push_back(std::string("Wearable Add"));
- if (!gAgentWearables.canAddWearable(mWearableType))
- {
- disabled_items.push_back(std::string("Wearable Add"));
- }
- }
- break;
- default:
- break;
- }
- }
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-// Called from menus
-// static
-bool LLWearableBridge::canWearOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return false;
- if(!self->isAgentInventory())
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
- if(!item || !item->isFinished()) return false;
- }
- return (!get_is_item_worn(self->mUUID));
-}
-
-// Called from menus
-// static
-void LLWearableBridge::onWearOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return;
- self->wearOnAvatar();
-}
-
-void LLWearableBridge::wearOnAvatar()
-{
- // TODO: investigate wearables may not be loaded at this point EXT-8231
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true);
- }
-}
-
-void LLWearableBridge::wearAddOnAvatar()
-{
- // TODO: investigate wearables may not be loaded at this point EXT-8231
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, false);
- }
-}
-
-// static
-void LLWearableBridge::onWearOnAvatarArrived( LLViewerWearable* wearable, void* userdata )
-{
- LLUUID* item_id = (LLUUID*) userdata;
- if(wearable)
- {
- LLViewerInventoryItem* item = NULL;
- item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
- if(item)
- {
- if(item->getAssetUUID() == wearable->getAssetID())
- {
- gAgentWearables.setWearableItem(item, wearable);
- gInventory.notifyObservers();
- //self->getFolderItem()->refreshFromRoot();
- }
- else
- {
- LL_INFOS() << "By the time wearable asset arrived, its inv item already pointed to a different asset." << LL_ENDL;
- }
- }
- }
- delete item_id;
-}
-
-// static
-// BAP remove the "add" code path once everything is fully COF-ified.
-void LLWearableBridge::onWearAddOnAvatarArrived( LLViewerWearable* wearable, void* userdata )
-{
- LLUUID* item_id = (LLUUID*) userdata;
- if(wearable)
- {
- LLViewerInventoryItem* item = NULL;
- item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
- if(item)
- {
- if(item->getAssetUUID() == wearable->getAssetID())
- {
- bool do_append = true;
- gAgentWearables.setWearableItem(item, wearable, do_append);
- gInventory.notifyObservers();
- //self->getFolderItem()->refreshFromRoot();
- }
- else
- {
- LL_INFOS() << "By the time wearable asset arrived, its inv item already pointed to a different asset." << LL_ENDL;
- }
- }
- }
- delete item_id;
-}
-
-// static
-bool LLWearableBridge::canEditOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(!self) return false;
-
- return (get_is_item_worn(self->mUUID));
-}
-
-// static
-void LLWearableBridge::onEditOnAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if(self)
- {
- self->editOnAvatar();
- }
-}
-
-void LLWearableBridge::editOnAvatar()
-{
- LLAgentWearables::editWearable(mUUID);
-}
-
-// static
-bool LLWearableBridge::canRemoveFromAvatar(void* user_data)
-{
- LLWearableBridge* self = (LLWearableBridge*)user_data;
- if( self && (LLAssetType::AT_BODYPART != self->mAssetType) )
- {
- return get_is_item_worn( self->mUUID );
- }
- return false;
-}
-
-void LLWearableBridge::removeFromAvatar()
-{
- LL_WARNS() << "safe to remove?" << LL_ENDL;
- if (get_is_item_worn(mUUID))
- {
- LLAppearanceMgr::instance().removeItemFromAvatar(mUUID);
- }
-}
-
-
-// +=================================================+
-// | LLLinkItemBridge |
-// +=================================================+
-// For broken item links
-
-std::string LLLinkItemBridge::sPrefix("Link: ");
-
-void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- // *TODO: Translate
- LL_DEBUGS() << "LLLink::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- items.push_back(std::string("Find Original"));
- disabled_items.push_back(std::string("Find Original"));
-
- if(isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Properties"));
- addDeleteContextMenuOptions(items, disabled_items);
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-// +=================================================+
-// | LLSettingsBridge |
-// +=================================================+
-
-LLSettingsBridge::LLSettingsBridge(LLInventoryPanel* inventory,
- LLFolderView* root,
- const LLUUID& uuid,
- LLSettingsType::type_e settings_type):
- LLItemBridge(inventory, root, uuid),
- mSettingsType(settings_type)
-{
-}
-
-LLUIImagePtr LLSettingsBridge::getIcon() const
-{
- return LLInventoryIcon::getIcon(LLAssetType::AT_SETTINGS, LLInventoryType::IT_SETTINGS, mSettingsType, false);
-}
-
-void LLSettingsBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("apply_settings_local" == action)
- {
- // Single item only
- LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
- if (!item)
- return;
- LLUUID asset_id = item->getAssetUUID();
- LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, asset_id, LLEnvironment::TRANSITION_INSTANT);
- LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL, LLEnvironment::TRANSITION_INSTANT);
- }
- else if ("apply_settings_parcel" == action)
- {
- // Single item only
- LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
- if (!item)
- return;
- LLUUID asset_id = item->getAssetUUID();
- std::string name = item->getName();
-
- U32 flags(0);
-
- if (!item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()))
- flags |= LLSettingsBase::FLAG_NOMOD;
- if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
- flags |= LLSettingsBase::FLAG_NOTRANS;
-
- LLParcel *parcel = LLViewerParcelMgr::instance().getAgentOrSelectedParcel();
- if (!parcel)
- {
- LL_WARNS("INVENTORY") << "could not identify parcel." << LL_ENDL;
- return;
- }
- S32 parcel_id = parcel->getLocalID();
-
- LL_DEBUGS("ENVIRONMENT") << "Applying asset ID " << asset_id << " to parcel " << parcel_id << LL_ENDL;
- LLEnvironment::instance().updateParcel(parcel_id, asset_id, name, LLEnvironment::NO_TRACK, -1, -1, flags);
- LLEnvironment::instance().setSharedEnvironment();
- }
- else
- LLItemBridge::performAction(model, action);
-}
-
-void LLSettingsBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- if (item->getPermissions().getOwner() != gAgent.getID())
- LLNotificationsUtil::add("NoEditFromLibrary");
- else
- LLInvFVBridgeAction::doAction(item->getType(), mUUID, getInventoryModel());
- }
-}
-
-void LLSettingsBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLSettingsBridge::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- if (isMarketplaceListingsFolder())
- {
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- hide_context_entries(menu, items, disabled_items);
- }
- else if (isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Share"));
- if (!canShare())
- {
- disabled_items.push_back(std::string("Share"));
- }
-
- addOpenRightClickMenuOption(items);
- items.push_back(std::string("Properties"));
-
- getClipboardEntries(true, items, disabled_items, flags);
-
- items.push_back("Settings Separator");
- items.push_back("Settings Apply Local");
-
- items.push_back("Settings Apply Parcel");
- if (!canUpdateParcel())
- disabled_items.push_back("Settings Apply Parcel");
-
- items.push_back("Settings Apply Region");
- if (!canUpdateRegion())
- disabled_items.push_back("Settings Apply Region");
- }
- addLinkReplaceMenuOption(items, disabled_items);
- hide_context_entries(menu, items, disabled_items);
-}
-
-bool LLSettingsBridge::renameItem(const std::string& new_name)
-{
- /*TODO: change internal settings name? */
- return LLItemBridge::renameItem(new_name);
-}
-
-bool LLSettingsBridge::isItemRenameable() const
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- return (item->getPermissions().allowModifyBy(gAgent.getID()));
- }
- return false;
-}
-
-bool LLSettingsBridge::canUpdateParcel() const
-{
- return LLEnvironment::instance().canAgentUpdateParcelEnvironment();
-}
-
-bool LLSettingsBridge::canUpdateRegion() const
-{
- return LLEnvironment::instance().canAgentUpdateRegionEnvironment();
-}
-
-
-// +=================================================+
-// | LLMaterialBridge |
-// +=================================================+
-
-void LLMaterialBridge::openItem()
-{
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
- }
-}
-
-void LLMaterialBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- LL_DEBUGS() << "LLMaterialBridge::buildContextMenu()" << LL_ENDL;
-
- if (isMarketplaceListingsFolder())
- {
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- addMarketplaceContextMenuOptions(flags, items, disabled_items);
- items.push_back(std::string("Properties"));
- getClipboardEntries(false, items, disabled_items, flags);
- hide_context_entries(menu, items, disabled_items);
- }
- else
- {
- LLItemBridge::buildContextMenu(menu, flags);
- }
-}
-
-
-// +=================================================+
-// | LLLinkBridge |
-// +=================================================+
-// For broken folder links.
-std::string LLLinkFolderBridge::sPrefix("Link: ");
-LLUIImagePtr LLLinkFolderBridge::getIcon() const
-{
- LLFolderType::EType folder_type = LLFolderType::FT_NONE;
- const LLInventoryObject *obj = getInventoryObject();
- if (obj)
- {
- LLViewerInventoryCategory* cat = NULL;
- LLInventoryModel* model = getInventoryModel();
- if(model)
- {
- cat = (LLViewerInventoryCategory*)model->getCategory(obj->getLinkedUUID());
- if (cat)
- {
- folder_type = cat->getPreferredType();
- }
- }
- }
- return LLFolderBridge::getIcon(folder_type);
-}
-
-void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- // *TODO: Translate
- LL_DEBUGS() << "LLLink::buildContextMenu()" << LL_ENDL;
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
-
- if (isItemInTrash())
- {
- addTrashContextMenuOptions(items, disabled_items);
- }
- else
- {
- items.push_back(std::string("Find Original"));
- addDeleteContextMenuOptions(items, disabled_items);
- }
- hide_context_entries(menu, items, disabled_items);
-}
-void LLLinkFolderBridge::performAction(LLInventoryModel* model, std::string action)
-{
- if ("goto" == action)
- {
- gotoItem();
- return;
- }
- LLItemBridge::performAction(model,action);
-}
-void LLLinkFolderBridge::gotoItem()
-{
- LLItemBridge::gotoItem();
-
- const LLUUID &cat_uuid = getFolderID();
- if (!cat_uuid.isNull())
- {
- LLFolderViewItem *base_folder = LLInventoryPanel::getActiveInventoryPanel()->getItemByID(cat_uuid);
- if (base_folder)
- {
- base_folder->setOpen(true);
- }
- }
-}
-const LLUUID &LLLinkFolderBridge::getFolderID() const
-{
- if (LLViewerInventoryItem *link_item = getItem())
- {
- if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory())
- {
- const LLUUID& cat_uuid = cat->getUUID();
- return cat_uuid;
- }
- }
- return LLUUID::null;
-}
-
-void LLUnknownItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- menuentry_vec_t items;
- menuentry_vec_t disabled_items;
- items.push_back(std::string("Properties"));
- items.push_back(std::string("Rename"));
- hide_context_entries(menu, items, disabled_items);
-}
-
-LLUIImagePtr LLUnknownItemBridge::getIcon() const
-{
- return LLInventoryIcon::getIcon(LLAssetType::AT_UNKNOWN, mInvType);
-}
-
-
-/********************************************************************************
- **
- ** BRIDGE ACTIONS
- **/
-
-// static
-void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type,
- const LLUUID& uuid,
- LLInventoryModel* model)
-{
- // Perform indirection in case of link.
- const LLUUID& linked_uuid = gInventory.getLinkedItemID(uuid);
-
- LLInvFVBridgeAction* action = createAction(asset_type,linked_uuid,model);
- if(action)
- {
- action->doIt();
- delete action;
- }
-}
-
-// static
-void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model)
-{
- llassert(model);
- LLViewerInventoryItem* item = model->getItem(uuid);
- llassert(item);
- if (item)
- {
- LLAssetType::EType asset_type = item->getType();
- LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
- if(action)
- {
- action->doIt();
- delete action;
- }
- }
-}
-
-LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const
-{
- if (mModel)
- return (LLViewerInventoryItem*)mModel->getItem(mUUID);
- return NULL;
-}
-
-class LLTextureBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- if (getItem())
- {
- LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLTextureBridgeAction(){}
-protected:
- LLTextureBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLSoundBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- send_sound_trigger(item->getAssetUUID(), SOUND_GAIN);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLSoundBridgeAction(){}
-protected:
- LLSoundBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLLandmarkBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- // Opening (double-clicking) a landmark immediately teleports,
- // but warns you the first time.
- LLSD payload;
- payload["asset_id"] = item->getAssetUUID();
-
- LLSD args;
- args["LOCATION"] = item->getName();
-
- LLNotificationsUtil::add("TeleportFromLandmark", args, payload);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLLandmarkBridgeAction(){}
-protected:
- LLLandmarkBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLCallingCardBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item && item->getCreatorUUID().notNull())
- {
- LLAvatarActions::showProfile(item->getCreatorUUID());
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLCallingCardBridgeAction(){}
-protected:
- LLCallingCardBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-
-};
-
-class LLNotecardBridgeAction
-: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLNotecardBridgeAction(){}
-protected:
- LLNotecardBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLGestureBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
- preview->setFocus(true);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLGestureBridgeAction(){}
-protected:
- LLGestureBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLAnimationBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLAnimationBridgeAction(){}
-protected:
- LLAnimationBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLObjectBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- attachOrDetach();
- }
- virtual ~LLObjectBridgeAction(){}
-protected:
- LLObjectBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
- void attachOrDetach();
-};
-
-void LLObjectBridgeAction::attachOrDetach()
-{
- if (get_is_item_worn(mUUID))
- {
- LLAppearanceMgr::instance().removeItemFromAvatar(mUUID);
- }
- else
- {
- LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding.
- }
-}
-
-class LLLSLTextBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLLSLTextBridgeAction(){}
-protected:
- LLLSLTextBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-class LLWearableBridgeAction: public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- wearOnAvatar();
- }
-
- virtual ~LLWearableBridgeAction(){}
-protected:
- LLWearableBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
- bool isItemInTrash() const;
- // return true if the item is in agent inventory. if false, it
- // must be lost or in the inventory library.
- bool isAgentInventory() const;
- void wearOnAvatar();
-};
-
-bool LLWearableBridgeAction::isItemInTrash() const
-{
- if(!mModel) return false;
- const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- return mModel->isObjectDescendentOf(mUUID, trash_id);
-}
-
-bool LLWearableBridgeAction::isAgentInventory() const
-{
- if(!mModel) return false;
- if(gInventory.getRootFolderID() == mUUID) return true;
- return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
-}
-
-void LLWearableBridgeAction::wearOnAvatar()
-{
- // TODO: investigate wearables may not be loaded at this point EXT-8231
-
- LLViewerInventoryItem* item = getItem();
- if(item)
- {
- if (get_is_item_worn(mUUID))
- {
- if(item->getType() != LLAssetType::AT_BODYPART)
- {
- LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID());
- }
- }
- else
- {
- LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true);
- }
- }
-}
-
-class LLSettingsBridgeAction
- : public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- virtual void doIt()
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLSettingsType::type_e type = item->getSettingsType();
- switch (type)
- {
- case LLSettingsType::ST_SKY:
- LLFloaterReg::showInstance("env_fixed_environmentent_sky", LLSDMap("inventory_id", item->getUUID()), TAKE_FOCUS_YES);
- break;
- case LLSettingsType::ST_WATER:
- LLFloaterReg::showInstance("env_fixed_environmentent_water", LLSDMap("inventory_id", item->getUUID()), TAKE_FOCUS_YES);
- break;
- case LLSettingsType::ST_DAYCYCLE:
- LLFloaterReg::showInstance("env_edit_extdaycycle", LLSDMap("inventory_id", item->getUUID())("edit_context", "inventory"), TAKE_FOCUS_YES);
- break;
- default:
- break;
- }
- }
- LLInvFVBridgeAction::doIt();
- }
- virtual ~LLSettingsBridgeAction(){}
-protected:
- LLSettingsBridgeAction(const LLUUID& id, LLInventoryModel* model) : LLInvFVBridgeAction(id, model) {}
-};
-
-class LLMaterialBridgeAction : public LLInvFVBridgeAction
-{
- friend class LLInvFVBridgeAction;
-public:
- void doIt() override
- {
- LLViewerInventoryItem* item = getItem();
- if (item)
- {
- LLFloaterReg::showInstance("material_editor", LLSD(item->getUUID()), TAKE_FOCUS_YES);
- }
- LLInvFVBridgeAction::doIt();
- }
- ~LLMaterialBridgeAction() = default;
-private:
- LLMaterialBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
-};
-
-
-LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type,
- const LLUUID& uuid,
- LLInventoryModel* model)
-{
- LLInvFVBridgeAction* action = NULL;
- switch(asset_type)
- {
- case LLAssetType::AT_TEXTURE:
- action = new LLTextureBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_SOUND:
- action = new LLSoundBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_LANDMARK:
- action = new LLLandmarkBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_CALLINGCARD:
- action = new LLCallingCardBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_OBJECT:
- action = new LLObjectBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_NOTECARD:
- action = new LLNotecardBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_ANIMATION:
- action = new LLAnimationBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_GESTURE:
- action = new LLGestureBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_LSL_TEXT:
- action = new LLLSLTextBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_BODYPART:
- action = new LLWearableBridgeAction(uuid,model);
- break;
- case LLAssetType::AT_SETTINGS:
- action = new LLSettingsBridgeAction(uuid, model);
- break;
- case LLAssetType::AT_MATERIAL:
- action = new LLMaterialBridgeAction(uuid, model);
- break;
- default:
- break;
- }
- return action;
-}
-
-/** Bridge Actions
- **
- ********************************************************************************/
-
-/************************************************************************/
-/* Recent Inventory Panel related classes */
-/************************************************************************/
-void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
-{
- menuentry_vec_t disabled_items, items;
- buildContextMenuOptions(flags, items, disabled_items);
-
- items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end());
-
- hide_context_entries(menu, items, disabled_items);
-}
-
-LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge(
- LLAssetType::EType asset_type,
- LLAssetType::EType actual_asset_type,
- LLInventoryType::EType inv_type,
- LLInventoryPanel* inventory,
- LLFolderViewModelInventory* view_model,
- LLFolderView* root,
- const LLUUID& uuid,
- U32 flags /*= 0x00*/ ) const
-{
- LLInvFVBridge* new_listener = NULL;
- if (asset_type == LLAssetType::AT_CATEGORY
- && actual_asset_type != LLAssetType::AT_LINK_FOLDER)
- {
- new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid);
- }
- else
- {
- new_listener = LLInventoryFolderViewModelBuilder::createBridge(asset_type,
- actual_asset_type,
- inv_type,
- inventory,
- view_model,
- root,
- uuid,
- flags);
- }
- return new_listener;
-}
-
-LLFolderViewGroupedItemBridge::LLFolderViewGroupedItemBridge()
-{
-}
-
-void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu)
-{
- uuid_vec_t ids;
- menuentry_vec_t disabled_items;
- if (get_selection_item_uuids(selected_items, ids))
- {
- if (!LLAppearanceMgr::instance().canAddWearables(ids) && canWearSelected(ids))
- {
- disabled_items.push_back(std::string("Wearable And Object Wear"));
- disabled_items.push_back(std::string("Wearable Add"));
- disabled_items.push_back(std::string("Attach To"));
- disabled_items.push_back(std::string("Attach To HUD"));
- }
- }
- disable_context_entries_if_present(menu, disabled_items);
-}
-
-bool LLFolderViewGroupedItemBridge::canWearSelected(const uuid_vec_t& item_ids) const
-{
- for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it)
- {
- const LLViewerInventoryItem* item = gInventory.getItem(*it);
- if (!item || (item->getType() >= LLAssetType::AT_COUNT) || (item->getType() <= LLAssetType::AT_NONE))
- {
- return false;
- }
- }
- return true;
-}
-// EOF
+/** + * @file llinventorybridge.cpp + * @brief Implementation of the Inventory-Folder-View-Bridge classes. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llinventorybridge.h" + +// external projects +#include "lltransfersourceasset.h" +#include "llavatarnamecache.h" // IDEVO + +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llattachmentsmgr.h" +#include "llavataractions.h" +#include "llfavoritesbar.h" // management of favorites folder +#include "llfloateropenobject.h" +#include "llfloaterreg.h" +#include "llfloatermarketplacelistings.h" +#include "llfloatersidepanelcontainer.h" +#include "llsidepanelinventory.h" +#include "llfloaterworldmap.h" +#include "llfolderview.h" +#include "llfriendcard.h" +#include "llgesturemgr.h" +#include "llgiveinventory.h" +#include "llfloaterimcontainer.h" +#include "llimview.h" +#include "llclipboard.h" +#include "llinventorydefines.h" +#include "llinventoryfunctions.h" +#include "llinventoryicon.h" +#include "llinventorymodel.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventorypanel.h" +#include "llmarketplacefunctions.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llpreviewanim.h" +#include "llpreviewgesture.h" +#include "llpreviewtexture.h" +#include "llselectmgr.h" +#include "llsidepanelappearance.h" +#include "lltooldraganddrop.h" +#include "lltrans.h" +#include "llurlaction.h" +#include "llviewerassettype.h" +#include "llviewerfoldertype.h" +#include "llviewermenu.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" +#include "llwearablelist.h" +#include "llwearableitemslist.h" +#include "lllandmarkactions.h" +#include "llpanellandmarks.h" +#include "llviewerparcelmgr.h" +#include "llparcel.h" + +#include "llenvironment.h" + +#include <boost/shared_ptr.hpp> + +void copy_slurl_to_clipboard_callback_inv(const std::string& slurl); + +const F32 SOUND_GAIN = 1.0f; +const F32 FOLDER_LOADING_MESSAGE_DELAY = 0.5f; // Seconds to wait before showing the LOADING... text in folder views + +using namespace LLOldEvents; + +// Function declarations +bool confirm_attachment_rez(const LLSD& notification, const LLSD& response); +void teleport_via_landmark(const LLUUID& asset_id); +static bool check_category(LLInventoryModel* model, + const LLUUID& cat_id, + LLInventoryPanel* active_panel, + LLInventoryFilter* filter); +static bool check_item(const LLUUID& item_id, + LLInventoryPanel* active_panel, + LLInventoryFilter* filter); + +// Helper functions + +bool isAddAction(const std::string& action) +{ + return ("wear" == action || "attach" == action || "activate" == action); +} + +bool isRemoveAction(const std::string& action) +{ + return ("take_off" == action || "detach" == action); +} + +bool isMarketplaceSendAction(const std::string& action) +{ + return ("send_to_marketplace" == action); +} + +bool isPanelActive(const std::string& panel_name) +{ + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(false); + return (active_panel && (active_panel->getName() == panel_name)); +} + +// Used by LLFolderBridge as callback for directory fetching recursion +class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver +{ +public: + LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids) : LLInventoryFetchDescendentsObserver(ids) {} + ~LLRightClickInventoryFetchDescendentsObserver() {} + virtual void execute(bool clear_observer = false); + virtual void done() + { + execute(true); + } +}; + +// Used by LLFolderBridge as callback for directory content items fetching +class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver +{ +public: + LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : LLInventoryFetchItemsObserver(ids) { }; + ~LLRightClickInventoryFetchObserver() {} + void execute(bool clear_observer = false) + { + if (clear_observer) + { + gInventory.removeObserver(this); + delete this; + } + // we've downloaded all the items, so repaint the dialog + LLFolderBridge::staticFolderOptionsMenu(); + } + virtual void done() + { + execute(true); + } +}; + +class LLPasteIntoFolderCallback: public LLInventoryCallback +{ +public: + LLPasteIntoFolderCallback(LLHandle<LLInventoryPanel>& handle) + : mInventoryPanel(handle) + { + } + ~LLPasteIntoFolderCallback() + { + processItems(); + } + + void fire(const LLUUID& inv_item) + { + mChangedIds.push_back(inv_item); + } + + void processItems() + { + LLInventoryPanel* panel = mInventoryPanel.get(); + bool has_elements = false; + for (LLUUID& inv_item : mChangedIds) + { + LLInventoryItem* item = gInventory.getItem(inv_item); + if (item && panel) + { + LLUUID root_id = panel->getRootFolderID(); + + if (inv_item == root_id) + { + return; + } + + LLFolderViewItem* item = panel->getItemByID(inv_item); + if (item) + { + if (!has_elements) + { + panel->clearSelection(); + panel->getRootFolder()->clearSelection(); + panel->getRootFolder()->requestArrange(); + panel->getRootFolder()->update(); + has_elements = true; + } + panel->getRootFolder()->changeSelection(item, true); + } + } + } + + if (has_elements) + { + panel->getRootFolder()->scrollToShowSelection(); + } + } +private: + LLHandle<LLInventoryPanel> mInventoryPanel; + std::vector<LLUUID> mChangedIds; +}; + +// +=================================================+ +// | LLInvFVBridge | +// +=================================================+ + +LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + mUUID(uuid), + mRoot(root), + mInvType(LLInventoryType::IT_NONE), + mIsLink(false), + LLFolderViewModelItemInventory(inventory->getRootViewModel()) +{ + mInventoryPanel = inventory->getInventoryPanelHandle(); + const LLInventoryObject* obj = getInventoryObject(); + mIsLink = obj && obj->getIsLinkType(); +} + +const std::string& LLInvFVBridge::getName() const +{ + const LLInventoryObject* obj = getInventoryObject(); + if(obj) + { + return obj->getName(); + } + return LLStringUtil::null; +} + +const std::string& LLInvFVBridge::getDisplayName() const +{ + if(mDisplayName.empty()) + { + buildDisplayName(); + } + return mDisplayName; +} + +std::string LLInvFVBridge::getSearchableDescription() const +{ + return get_searchable_description(getInventoryModel(), mUUID); +} + +std::string LLInvFVBridge::getSearchableCreatorName() const +{ + return get_searchable_creator_name(getInventoryModel(), mUUID); +} + +std::string LLInvFVBridge::getSearchableUUIDString() const +{ + return get_searchable_UUID(getInventoryModel(), mUUID); +} + +// Folders have full perms +PermissionMask LLInvFVBridge::getPermissionMask() const +{ + return PERM_ALL; +} + +// virtual +LLFolderType::EType LLInvFVBridge::getPreferredType() const +{ + return LLFolderType::FT_NONE; +} + + +// Folders don't have creation dates. +time_t LLInvFVBridge::getCreationDate() const +{ + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + return objectp->getCreationDate(); + } + return (time_t)0; +} + +void LLInvFVBridge::setCreationDate(time_t creation_date_utc) +{ + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + objectp->setCreationDate(creation_date_utc); + } +} + + +// Can be destroyed (or moved to trash) +bool LLInvFVBridge::isItemRemovable(bool check_worn) const +{ + return get_is_item_removable(getInventoryModel(), mUUID, check_worn); +} + +// Can be moved to another folder +bool LLInvFVBridge::isItemMovable() const +{ + return true; +} + +bool LLInvFVBridge::isLink() const +{ + return mIsLink; +} + +bool LLInvFVBridge::isLibraryItem() const +{ + return gInventory.isObjectDescendentOf(getUUID(),gInventory.getLibraryRootFolderID()); +} + +/*virtual*/ +/** + * @brief Adds this item into clipboard storage + */ +bool LLInvFVBridge::cutToClipboard() +{ + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemMovable() && isItemRemovable()) + { + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const bool cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id); + + if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) || + LLMarketplaceData::instance().isListedAndActive(mUUID))) + { + LLUUID parent_uuid = obj->getParentUUID(); + bool result = perform_cutToClipboard(); + gInventory.addChangedMask(LLInventoryObserver::STRUCTURE, parent_uuid); + return result; + } + else + { + // Otherwise just perform the cut + return perform_cutToClipboard(); + } + } + return false; +} + +// virtual +bool LLInvFVBridge::isCutToClipboard() +{ + if (LLClipboard::instance().isCutMode()) + { + return LLClipboard::instance().isOnClipboard(mUUID); + } + return false; +} + +// Callback for cutToClipboard if DAMA required... +bool LLInvFVBridge::callback_cutToClipboard(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + return perform_cutToClipboard(); + } + return false; +} + +bool LLInvFVBridge::perform_cutToClipboard() +{ + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemMovable() && isItemRemovable()) + { + LLClipboard::instance().setCutMode(true); + return LLClipboard::instance().addToClipboard(mUUID); + } + return false; +} + +bool LLInvFVBridge::copyToClipboard() const +{ + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemCopyable()) + { + return LLClipboard::instance().addToClipboard(mUUID); + } + return false; +} + +void LLInvFVBridge::showProperties() +{ + if (isMarketplaceListingsFolder()) + { + LLFloaterReg::showInstance("item_properties", LLSD().with("id",mUUID),true); + // Force it to show on top as this floater has a tendency to hide when confirmation dialog shows up + LLFloater* floater_properties = LLFloaterReg::findInstance("item_properties", LLSD().with("id",mUUID)); + if (floater_properties) + { + floater_properties->setVisibleAndFrontmost(); + } + } + else + { + show_item_profile(mUUID); + } +} + +void LLInvFVBridge::navigateToFolder(bool new_window, bool change_mode) +{ + if(new_window) + { + mInventoryPanel.get()->openSingleViewInventory(mUUID); + } + else + { + if(change_mode) + { + LLInventoryPanel::setSFViewAndOpenFolder(mInventoryPanel.get(), mUUID); + } + else + { + LLInventorySingleFolderPanel* panel = dynamic_cast<LLInventorySingleFolderPanel*>(mInventoryPanel.get()); + if (!panel || !getInventoryModel() || mUUID.isNull()) + { + return; + } + + panel->changeFolderRoot(mUUID); + } + + } +} + +void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) +{ + // Deactivate gestures when moving them into Trash + LLInvFVBridge* bridge; + LLInventoryModel* model = getInventoryModel(); + LLViewerInventoryItem* item = NULL; + LLViewerInventoryCategory* cat = NULL; + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + S32 count = batch.size(); + S32 i,j; + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch[i]); + if(!bridge || !bridge->isItemRemovable()) continue; + item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); + if (item) + { + if(LLAssetType::AT_GESTURE == item->getType()) + { + LLGestureMgr::instance().deactivateGesture(item->getUUID()); + } + } + } + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch[i]); + if(!bridge || !bridge->isItemRemovable()) continue; + cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); + if (cat) + { + gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, false ); + for (j=0; j<descendent_items.size(); j++) + { + if(LLAssetType::AT_GESTURE == descendent_items[j]->getType()) + { + LLGestureMgr::instance().deactivateGesture(descendent_items[j]->getUUID()); + } + } + } + } + removeBatchNoCheck(batch); + model->checkTrashOverflow(); +} + +void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch) +{ + // this method moves a bunch of items and folders to the trash. As + // per design guidelines for the inventory model, the message is + // built and the accounting is performed first. After all of that, + // we call LLInventoryModel::moveObject() to move everything + // around. + LLInvFVBridge* bridge; + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + LLMessageSystem* msg = gMessageSystem; + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + LLViewerInventoryItem* item = NULL; + uuid_vec_t move_ids; + LLInventoryModel::update_map_t update; + bool start_new_message = true; + S32 count = batch.size(); + S32 i; + + // first, hide any 'preview' floaters that correspond to the items + // being deleted. + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch[i]); + if(!bridge || !bridge->isItemRemovable()) continue; + item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); + if(item) + { + LLPreview::hide(item->getUUID()); + } + } + + // do the inventory move to trash + + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch[i]); + if(!bridge || !bridge->isItemRemovable()) continue; + item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); + if(item) + { + if(item->getParentUUID() == trash_id) continue; + move_ids.push_back(item->getUUID()); + --update[item->getParentUUID()]; + ++update[trash_id]; + if(start_new_message) + { + start_new_message = false; + msg->newMessageFast(_PREHASH_MoveInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addBOOLFast(_PREHASH_Stamp, true); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_ItemID, item->getUUID()); + msg->addUUIDFast(_PREHASH_FolderID, trash_id); + msg->addString("NewName", NULL); + if(msg->isSendFullFast(_PREHASH_InventoryData)) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); + } + } + } + if(!start_new_message) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); + } + + for(i = 0; i < count; ++i) + { + bridge = (LLInvFVBridge*)(batch[i]); + if(!bridge || !bridge->isItemRemovable()) continue; + LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); + if(cat) + { + if(cat->getParentUUID() == trash_id) continue; + move_ids.push_back(cat->getUUID()); + --update[cat->getParentUUID()]; + ++update[trash_id]; + if(start_new_message) + { + start_new_message = false; + msg->newMessageFast(_PREHASH_MoveInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addBOOL("Stamp", true); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID()); + msg->addUUIDFast(_PREHASH_ParentID, trash_id); + if(msg->isSendFullFast(_PREHASH_InventoryData)) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); + } + } + } + if(!start_new_message) + { + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + } + + // move everything. + uuid_vec_t::iterator it = move_ids.begin(); + uuid_vec_t::iterator end = move_ids.end(); + for(; it != end; ++it) + { + gInventory.moveObject((*it), trash_id); + LLViewerInventoryItem* item = gInventory.getItem(*it); + if (item) + { + model->updateItem(item); + } + } + + // notify inventory observers. + model->notifyObservers(); +} + +bool LLInvFVBridge::isClipboardPasteable() const +{ + // Return false on degenerated cases: empty clipboard, no inventory, no agent + if (!LLClipboard::instance().hasContents() || !isAgentInventory()) + { + return false; + } + LLInventoryModel* model = getInventoryModel(); + if (!model) + { + return false; + } + + // In cut mode, whatever is on the clipboard is always pastable + if (LLClipboard::instance().isCutMode()) + { + return true; + } + + // In normal mode, we need to check each element of the clipboard to know if we can paste or not + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + S32 count = objects.size(); + for(S32 i = 0; i < count; i++) + { + const LLUUID &item_id = objects.at(i); + + // Folders are pastable if all items in there are copyable + const LLInventoryCategory *cat = model->getCategory(item_id); + if (cat) + { + LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, item_id); + if (!cat_br.isItemCopyable(false)) + return false; + // Skip to the next item in the clipboard + continue; + } + + // Each item must be copyable to be pastable + LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id); + if (!item_br.isItemCopyable(false)) + { + return false; + } + } + return true; +} + +bool LLInvFVBridge::isClipboardPasteableAsLink() const +{ + if (!LLClipboard::instance().hasContents() || !isAgentInventory()) + { + return false; + } + const LLInventoryModel* model = getInventoryModel(); + if (!model) + { + return false; + } + + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + S32 count = objects.size(); + for(S32 i = 0; i < count; i++) + { + const LLInventoryItem *item = model->getItem(objects.at(i)); + if (item) + { + if (!LLAssetType::lookupCanLink(item->getActualType())) + { + return false; + } + + if (gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getLibraryRootFolderID())) + { + return false; + } + } + const LLViewerInventoryCategory *cat = model->getCategory(objects.at(i)); + if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + { + return false; + } + } + return true; +} + +void disable_context_entries_if_present(LLMenuGL& menu, + const menuentry_vec_t &disabled_entries) +{ + const LLView::child_list_t *list = menu.getChildList(); + for (LLView::child_list_t::const_iterator itor = list->begin(); + itor != list->end(); + ++itor) + { + LLView *menu_item = (*itor); + std::string name = menu_item->getName(); + + // descend into split menus: + LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item); + if ((name == "More") && branchp) + { + disable_context_entries_if_present(*branchp->getBranch(), disabled_entries); + } + + bool found = false; + menuentry_vec_t::const_iterator itor2; + for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) + { + if (*itor2 == name) + { + found = true; + break; + } + } + + if (found) + { + menu_item->setVisible(true); + // A bit of a hack so we can remember that some UI element explicitly set this to be visible + // so that some other UI element from multi-select doesn't later set this invisible. + menu_item->pushVisible(true); + + menu_item->setEnabled(false); + } + } +} +void hide_context_entries(LLMenuGL& menu, + const menuentry_vec_t &entries_to_show, + const menuentry_vec_t &disabled_entries) +{ + const LLView::child_list_t *list = menu.getChildList(); + + // For removing double separators or leading separator. Start at true so that + // if the first element is a separator, it will not be shown. + bool is_previous_entry_separator = true; + + for (LLView::child_list_t::const_iterator itor = list->begin(); + itor != list->end(); + ++itor) + { + LLView *menu_item = (*itor); + std::string name = menu_item->getName(); + + // descend into split menus: + LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item); + if (((name == "More") || (name == "create_new")) && branchp) + { + hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries); + } + + bool found = false; + + menuentry_vec_t::const_iterator itor2 = std::find(entries_to_show.begin(), entries_to_show.end(), name); + if (itor2 != entries_to_show.end()) + { + found = true; + } + + // Don't allow multiple separators in a row (e.g. such as if there are no items + // between two separators). + if (found) + { + const bool is_entry_separator = (dynamic_cast<LLMenuItemSeparatorGL *>(menu_item) != NULL); + found = !(is_entry_separator && is_previous_entry_separator); + is_previous_entry_separator = is_entry_separator; + } + + if (!found) + { + if (!menu_item->getLastVisible()) + { + menu_item->setVisible(false); + } + + if (menu_item->getEnabled()) + { + // These should stay enabled unless specifically disabled + const menuentry_vec_t exceptions = { + "Detach From Yourself", + "Wearable And Object Wear", + "Wearable Add", + }; + + menuentry_vec_t::const_iterator itor2 = std::find(exceptions.begin(), exceptions.end(), name); + if (itor2 == exceptions.end()) + { + menu_item->setEnabled(false); + } + } + } + else + { + menu_item->setVisible(true); + // A bit of a hack so we can remember that some UI element explicitly set this to be visible + // so that some other UI element from multi-select doesn't later set this invisible. + menu_item->pushVisible(true); + + bool enabled = true; + for (itor2 = disabled_entries.begin(); enabled && (itor2 != disabled_entries.end()); ++itor2) + { + enabled &= (*itor2 != name); + } + + menu_item->setEnabled(enabled); + } + } +} + +// Helper for commonly-used entries +void LLInvFVBridge::getClipboardEntries(bool show_asset_id, + menuentry_vec_t &items, + menuentry_vec_t &disabled_items, U32 flags) +{ + const LLInventoryObject *obj = getInventoryObject(); + bool single_folder_root = (mRoot == NULL); + + if (obj) + { + + if (obj->getType() != LLAssetType::AT_CATEGORY) + { + items.push_back(std::string("Copy Separator")); + } + items.push_back(std::string("Copy")); + if (!isItemCopyable()) + { + disabled_items.push_back(std::string("Copy")); + } + + if (isAgentInventory() && !single_folder_root) + { + items.push_back(std::string("New folder from selected")); + items.push_back(std::string("Subfolder Separator")); + std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); + uuid_vec_t ids; + std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); + if (!is_only_items_selected(ids) && !is_only_cats_selected(ids)) + { + disabled_items.push_back(std::string("New folder from selected")); + } + } + + if (obj->getIsLinkType()) + { + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + + items.push_back(std::string("Cut")); + if (!isItemMovable() || !canMenuCut()) + { + disabled_items.push_back(std::string("Cut")); + } + } + else + { + if (LLAssetType::lookupCanLink(obj->getType())) + { + items.push_back(std::string("Find Links")); + } + + if (!isInboxFolder() && !single_folder_root) + { + items.push_back(std::string("Rename")); + if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0)) + { + disabled_items.push_back(std::string("Rename")); + } + } + + items.push_back(std::string("thumbnail")); + if (isLibraryItem()) + { + disabled_items.push_back(std::string("thumbnail")); + } + + LLViewerInventoryItem *inv_item = gInventory.getItem(mUUID); + if (show_asset_id) + { + items.push_back(std::string("Copy Asset UUID")); + + bool is_asset_knowable = false; + + if (inv_item) + { + is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(inv_item->getType()); + } + if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308 + || (! ( isItemPermissive() || gAgent.isGodlike() ) ) + || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Copy Asset UUID")); + } + } + + if(!single_folder_root) + { + items.push_back(std::string("Cut")); + if (!isItemMovable() || !canMenuCut()) + { + disabled_items.push_back(std::string("Cut")); + } + + if (canListOnMarketplace() && !isMarketplaceListingsFolder() && !isInboxFolder()) + { + items.push_back(std::string("Marketplace Separator")); + + if (gMenuHolder->getChild<LLView>("MarketplaceListings")->getVisible()) + { + items.push_back(std::string("Marketplace Copy")); + items.push_back(std::string("Marketplace Move")); + if (!canListOnMarketplaceNow()) + { + disabled_items.push_back(std::string("Marketplace Copy")); + disabled_items.push_back(std::string("Marketplace Move")); + } + } + } + } + } + } + + // Don't allow items to be pasted directly into the COF or the inbox + if (!isCOFFolder() && !isInboxFolder()) + { + items.push_back(std::string("Paste")); + } + if (!isClipboardPasteable() || ((flags & FIRST_SELECTED_ITEM) == 0)) + { + disabled_items.push_back(std::string("Paste")); + } + + static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); + if (inventory_linking) + { + items.push_back(std::string("Paste As Link")); + if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Paste As Link")); + } + } + + if (obj->getType() != LLAssetType::AT_CATEGORY) + { + items.push_back(std::string("Paste Separator")); + } + + if(!single_folder_root) + { + addDeleteContextMenuOptions(items, disabled_items); + } + + if (!isPanelActive("All Items") && !isPanelActive("comb_single_folder_inv")) + { + items.push_back(std::string("Show in Main Panel")); + } +} + +void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLInvFVBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + + addOpenRightClickMenuOption(items); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +bool get_selection_item_uuids(LLFolderView::selected_items_t& selected_items, uuid_vec_t& ids) +{ + uuid_vec_t results; + S32 non_item = 0; + for(LLFolderView::selected_items_t::iterator it = selected_items.begin(); it != selected_items.end(); ++it) + { + LLItemBridge *view_model = dynamic_cast<LLItemBridge *>((*it)->getViewModelItem()); + + if(view_model && view_model->getUUID().notNull()) + { + results.push_back(view_model->getUUID()); + } + else + { + non_item++; + } + } + if (non_item == 0) + { + ids = results; + return true; + } + return false; +} + +void LLInvFVBridge::addTrashContextMenuOptions(menuentry_vec_t &items, + menuentry_vec_t &disabled_items) +{ + const LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + items.push_back(std::string("Find Original")); + if (isLinkedObjectMissing()) + { + disabled_items.push_back(std::string("Find Original")); + } + } + items.push_back(std::string("Purge Item")); + if (!isItemRemovable()) + { + disabled_items.push_back(std::string("Purge Item")); + } + items.push_back(std::string("Restore Item")); +} + +void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items, + menuentry_vec_t &disabled_items) +{ + + const LLInventoryObject *obj = getInventoryObject(); + + // Don't allow delete as a direct option from COF folder. + if (obj && obj->getIsLinkType() && isCOFFolder() && get_is_item_worn(mUUID)) + { + return; + } + + items.push_back(std::string("Delete")); + + if (isPanelActive("Favorite Items") || !canMenuDelete()) + { + disabled_items.push_back(std::string("Delete")); + } +} + +void LLInvFVBridge::addOpenRightClickMenuOption(menuentry_vec_t &items) +{ + const LLInventoryObject *obj = getInventoryObject(); + const bool is_link = (obj && obj->getIsLinkType()); + + if (is_link) + items.push_back(std::string("Open Original")); + else + items.push_back(std::string("Open")); +} + +void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, + menuentry_vec_t &items, + menuentry_vec_t &disabled_items) +{ + S32 depth = depth_nesting_in_marketplace(mUUID); + if (depth == 1) + { + // Options available at the Listing Folder level + items.push_back(std::string("Marketplace Create Listing")); + items.push_back(std::string("Marketplace Associate Listing")); + items.push_back(std::string("Marketplace Check Listing")); + items.push_back(std::string("Marketplace List")); + items.push_back(std::string("Marketplace Unlist")); + if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0)) + { + // During SLM update, disable all marketplace related options + // Also disable all if multiple selected items + disabled_items.push_back(std::string("Marketplace Create Listing")); + disabled_items.push_back(std::string("Marketplace Associate Listing")); + disabled_items.push_back(std::string("Marketplace Check Listing")); + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + } + else + { + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + items.push_back(std::string("Marketplace Get Listing")); + } + if (LLMarketplaceData::instance().isListed(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Create Listing")); + disabled_items.push_back(std::string("Marketplace Associate Listing")); + if (LLMarketplaceData::instance().getVersionFolder(mUUID).isNull()) + { + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + } + else + { + if (LLMarketplaceData::instance().getActivationState(mUUID)) + { + disabled_items.push_back(std::string("Marketplace List")); + } + else + { + disabled_items.push_back(std::string("Marketplace Unlist")); + } + } + } + else + { + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + disabled_items.push_back(std::string("Marketplace Get Listing")); + } + } + } + } + if (depth == 2) + { + // Options available at the Version Folder levels and only for folders + LLInventoryCategory* cat = gInventory.getCategory(mUUID); + if (cat && LLMarketplaceData::instance().isListed(cat->getParentUUID())) + { + items.push_back(std::string("Marketplace Activate")); + items.push_back(std::string("Marketplace Deactivate")); + if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0)) + { + // During SLM update, disable all marketplace related options + // Also disable all if multiple selected items + disabled_items.push_back(std::string("Marketplace Activate")); + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + else + { + if (LLMarketplaceData::instance().isVersionFolder(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Activate")); + if (LLMarketplaceData::instance().getActivationState(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + } + else + { + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + } + } + } + + items.push_back(std::string("Marketplace Edit Listing")); + LLUUID listing_folder_id = nested_parent_id(mUUID,depth); + LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(listing_folder_id); + + if (depth >= 2) + { + // Prevent creation of new folders if the max count has been reached on this version folder (active or not) + LLUUID local_version_folder_id = nested_parent_id(mUUID,depth-1); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(local_version_folder_id, categories, items, false); + static LLCachedControl<U32> max_depth(gSavedSettings, "InventoryOutboxMaxFolderDepth", 4); + static LLCachedControl<U32> max_count(gSavedSettings, "InventoryOutboxMaxFolderCount", 20); + if (categories.size() >= max_count + || depth > (max_depth + 1)) + { + disabled_items.push_back(std::string("New Folder")); + } + } + + // Options available at all levels on items and categories + if (!LLMarketplaceData::instance().isListed(listing_folder_id) || version_folder_id.isNull()) + { + disabled_items.push_back(std::string("Marketplace Edit Listing")); + } + + // Separator + items.push_back(std::string("Marketplace Listings Separator")); +} + +void LLInvFVBridge::addLinkReplaceMenuOption(menuentry_vec_t& items, menuentry_vec_t& disabled_items) +{ + const LLInventoryObject* obj = getInventoryObject(); + + if (isAgentInventory() && obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getType() != LLAssetType::AT_LINK_FOLDER) + { + items.push_back(std::string("Replace Links")); + + if (mRoot->getSelectedCount() != 1) + { + disabled_items.push_back(std::string("Replace Links")); + } + } +} + +bool LLInvFVBridge::canMenuDelete() +{ + return isItemRemovable(false); +} + +bool LLInvFVBridge::canMenuCut() +{ + return isItemRemovable(true); +} + +// *TODO: remove this +bool LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const +{ + bool rv = false; + + const LLInventoryObject* obj = getInventoryObject(); + + if(obj) + { + *type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType()); + if(*type == DAD_NONE) + { + return false; + } + + *id = obj->getUUID(); + //object_ids.push_back(obj->getUUID()); + + if (*type == DAD_CATEGORY) + { + LLInventoryModelBackgroundFetch::instance().start(obj->getUUID()); + } + + rv = true; + } + + return rv; +} + +LLInventoryObject* LLInvFVBridge::getInventoryObject() const +{ + LLInventoryObject* obj = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + obj = (LLInventoryObject*)model->getObject(mUUID); + } + return obj; +} + +LLInventoryModel* LLInvFVBridge::getInventoryModel() const +{ + LLInventoryPanel* panel = mInventoryPanel.get(); + return panel ? panel->getModel() : NULL; +} + +LLInventoryFilter* LLInvFVBridge::getInventoryFilter() const +{ + LLInventoryPanel* panel = mInventoryPanel.get(); + return panel ? &(panel->getFilter()) : NULL; +} + +bool LLInvFVBridge::isItemInTrash() const +{ + LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + return model->isObjectDescendentOf(mUUID, trash_id); +} + +bool LLInvFVBridge::isLinkedObjectInTrash() const +{ + if (isItemInTrash()) return true; + + const LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id); + } + return false; +} + +bool LLInvFVBridge::isItemInOutfits() const +{ + const LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + + const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + return isCOFFolder() || (my_outfits_cat == mUUID) || model->isObjectDescendentOf(mUUID, my_outfits_cat); +} + +bool LLInvFVBridge::isLinkedObjectMissing() const +{ + const LLInventoryObject *obj = getInventoryObject(); + if (!obj) + { + return true; + } + if (obj->getIsLinkType() && LLAssetType::lookupIsLinkType(obj->getType())) + { + return true; + } + return false; +} + +bool LLInvFVBridge::isAgentInventory() const +{ + const LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + if(gInventory.getRootFolderID() == mUUID) return true; + return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()); +} + +bool LLInvFVBridge::isCOFFolder() const +{ + return LLAppearanceMgr::instance().getIsInCOF(mUUID); +} + +// *TODO : Suppress isInboxFolder() once Merchant Outbox is fully deprecated +bool LLInvFVBridge::isInboxFolder() const +{ + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); + + if (inbox_id.isNull()) + { + return false; + } + + return gInventory.isObjectDescendentOf(mUUID, inbox_id); +} + +bool LLInvFVBridge::isMarketplaceListingsFolder() const +{ + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + + if (folder_id.isNull()) + { + return false; + } + + return gInventory.isObjectDescendentOf(mUUID, folder_id); +} + +bool LLInvFVBridge::isItemPermissive() const +{ + return false; +} + +// static +void LLInvFVBridge::changeItemParent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + bool restamp) +{ + model->changeItemParent(item, new_parent_id, restamp); +} + +// static +void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& new_parent_id, + bool restamp) +{ + model->changeCategoryParent(cat, new_parent_id, restamp); +} + +LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, + LLFolderView* root, + const LLUUID& uuid, + U32 flags) +{ + LLInvFVBridge* new_listener = NULL; + switch(asset_type) + { + case LLAssetType::AT_TEXTURE: + if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLTextureBridge(inventory, root, uuid, inv_type); + break; + + case LLAssetType::AT_SOUND: + if(!(inv_type == LLInventoryType::IT_SOUND)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLSoundBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_LANDMARK: + if(!(inv_type == LLInventoryType::IT_LANDMARK)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLLandmarkBridge(inventory, root, uuid, flags); + break; + + case LLAssetType::AT_CALLINGCARD: + if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLCallingCardBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_SCRIPT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLItemBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_OBJECT: + if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLObjectBridge(inventory, root, uuid, inv_type, flags); + break; + + case LLAssetType::AT_NOTECARD: + if(!(inv_type == LLInventoryType::IT_NOTECARD)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLNotecardBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_ANIMATION: + if(!(inv_type == LLInventoryType::IT_ANIMATION)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLAnimationBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_GESTURE: + if(!(inv_type == LLInventoryType::IT_GESTURE)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLGestureBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_LSL_TEXT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLLSLTextBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + if(!(inv_type == LLInventoryType::IT_WEARABLE)) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, LLWearableType::inventoryFlagsToWearableType(flags)); + break; + case LLAssetType::AT_CATEGORY: + if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) + { + // Create a link folder handler instead + new_listener = new LLLinkFolderBridge(inventory, root, uuid); + } + else if (actual_asset_type == LLAssetType::AT_MARKETPLACE_FOLDER) + { + // Create a marketplace folder handler + new_listener = new LLMarketplaceFolderBridge(inventory, root, uuid); + } + else + { + new_listener = new LLFolderBridge(inventory, root, uuid); + } + break; + case LLAssetType::AT_LINK: + case LLAssetType::AT_LINK_FOLDER: + // Only should happen for broken links. + new_listener = new LLLinkItemBridge(inventory, root, uuid); + break; + case LLAssetType::AT_UNKNOWN: + new_listener = new LLUnknownItemBridge(inventory, root, uuid); + break; + case LLAssetType::AT_IMAGE_TGA: + case LLAssetType::AT_IMAGE_JPEG: + //LL_WARNS() << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << LL_ENDL; + break; + + case LLAssetType::AT_SETTINGS: + if (inv_type != LLInventoryType::IT_SETTINGS) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLSettingsBridge(inventory, root, uuid, LLSettingsType::fromInventoryFlags(flags)); + break; + + case LLAssetType::AT_MATERIAL: + if (inv_type != LLInventoryType::IT_MATERIAL) + { + LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL; + } + new_listener = new LLMaterialBridge(inventory, root, uuid); + break; + + default: + LL_INFOS_ONCE() << "Unhandled asset type (llassetstorage.h): " + << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << LL_ENDL; + break; + } + + if (new_listener) + { + new_listener->mInvType = inv_type; + } + + return new_listener; +} + +void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) +{ + LLInventoryObject* obj = model->getObject(uuid); + if (obj) + { + remove_inventory_object(uuid, NULL); + } +} + +void LLInvFVBridge::removeObject(LLInventoryModel *model, const LLUUID &uuid) +{ + // Keep track of the parent + LLInventoryItem* itemp = model->getItem(uuid); + LLUUID parent_id = (itemp ? itemp->getParentUUID() : LLUUID::null); + // Remove the object + model->removeObject(uuid); + // Get the parent updated + if (parent_id.notNull()) + { + LLViewerInventoryCategory* parent_cat = model->getCategory(parent_id); + model->updateCategory(parent_cat); + model->notifyObservers(); + } +} + +bool LLInvFVBridge::canShare() const +{ + bool can_share = false; + + if (isAgentInventory()) + { + const LLInventoryModel* model = getInventoryModel(); + if (model) + { + const LLViewerInventoryItem *item = model->getItem(mUUID); + if (item) + { + if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) + { + can_share = LLGiveInventory::isInventoryGiveAcceptable(item); + } + } + else + { + // Categories can be given. + can_share = (model->getCategory(mUUID) != NULL); + } + + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if ((mUUID == trash_id) || gInventory.isObjectDescendentOf(mUUID, trash_id)) + { + can_share = false; + } + } + } + + return can_share; +} + +bool LLInvFVBridge::canListOnMarketplace() const +{ + LLInventoryModel * model = getInventoryModel(); + + LLViewerInventoryCategory * cat = model->getCategory(mUUID); + if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + { + return false; + } + + if (!isAgentInventory()) + { + return false; + } + + LLViewerInventoryItem * item = model->getItem(mUUID); + if (item) + { + if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + { + return false; + } + + if (LLAssetType::AT_CALLINGCARD == item->getType()) + { + return false; + } + } + + return true; +} + +bool LLInvFVBridge::canListOnMarketplaceNow() const +{ + bool can_list = true; + + const LLInventoryObject* obj = getInventoryObject(); + can_list &= (obj != NULL); + + if (can_list) + { + const LLUUID& object_id = obj->getLinkedUUID(); + can_list = object_id.notNull(); + + if (can_list) + { + LLFolderViewFolder * object_folderp = mInventoryPanel.get() ? mInventoryPanel.get()->getFolderByID(object_id) : NULL; + if (object_folderp) + { + can_list = !static_cast<LLFolderBridge*>(object_folderp->getViewModelItem())->isLoading(); + } + } + + if (can_list) + { + std::string error_msg; + LLInventoryModel* model = getInventoryModel(); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (marketplacelistings_id.notNull()) + { + LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id); + LLInventoryCategory *cat = model->getCategory(mUUID); + if (cat) + { + can_list = can_move_folder_to_marketplace(master_folder, master_folder, cat, error_msg); + } + else + { + LLInventoryItem *item = model->getItem(mUUID); + can_list = (item ? can_move_item_to_marketplace(master_folder, master_folder, item, error_msg) : false); + } + } + else + { + can_list = false; + } + } + } + + return can_list; +} + +LLToolDragAndDrop::ESource LLInvFVBridge::getDragSource() const +{ + if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_AGENT; + } + else if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_LIBRARY; + } + + return LLToolDragAndDrop::SOURCE_VIEWER; +} + + + +// +=================================================+ +// | InventoryFVBridgeBuilder | +// +=================================================+ +LLInvFVBridge* LLInventoryFolderViewModelBuilder::createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, + LLFolderView* root, + const LLUUID& uuid, + U32 flags /* = 0x00 */) const +{ + return LLInvFVBridge::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + view_model, + root, + uuid, + flags); +} + +// +=================================================+ +// | LLItemBridge | +// +=================================================+ + +void LLItemBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("goto" == action) + { + gotoItem(); + } + + if ("open" == action || "open_original" == action) + { + openItem(); + return; + } + else if ("properties" == action) + { + showProperties(); + return; + } + else if ("purge" == action) + { + purgeItem(model, mUUID); + return; + } + else if ("restoreToWorld" == action) + { + restoreToWorld(); + return; + } + else if ("restore" == action) + { + restoreItem(); + return; + } + else if ("thumbnail" == action) + { + LLSD data(mUUID); + LLFloaterReg::showInstance("change_item_thumbnail", data); + return; + } + else if ("copy_uuid" == action) + { + // Single item only + LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); + if(!item) return; + LLUUID asset_id = item->getProtectedAssetUUID(); + std::string buffer; + asset_id.toString(buffer); + + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer)); + return; + } + else if ("show_in_main_panel" == action) + { + LLInventoryPanel::openInventoryPanelAndSetSelection(true, mUUID, true); + return; + } + else if ("cut" == action) + { + cutToClipboard(); + return; + } + else if ("copy" == action) + { + copyToClipboard(); + return; + } + else if ("paste" == action) + { + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); + if (!folder_view_itemp) return; + + folder_view_itemp->getViewModelItem()->pasteFromClipboard(); + return; + } + else if ("paste_link" == action) + { + // Single item only + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); + if (!folder_view_itemp) return; + + folder_view_itemp->getViewModelItem()->pasteLinkFromClipboard(); + return; + } + else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) + { + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + // Note: For a single item, if it's not a copy, then it's a move + move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action)); + } + else if ("copy_slurl" == action) + { + LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); + if(item) + { + LLUUID asset_id = item->getAssetUUID(); + LLLandmark* landmark = gLandmarkList.getAsset(asset_id); + if (landmark) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + LLLandmarkActions::getSLURLfromPosGlobal(global_pos, ©_slurl_to_clipboard_callback_inv, true); + } + } + } + else if ("show_on_map" == action) + { + doActionOnCurSelectedLandmark(boost::bind(&LLItemBridge::doShowOnMap, this, _1)); + } + else if ("marketplace_edit_listing" == action) + { + std::string url = LLMarketplaceData::instance().getListingURL(mUUID); + LLUrlAction::openURL(url); + } +} + +void LLItemBridge::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb) +{ + LLViewerInventoryItem* cur_item = getItem(); + if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK) + { + LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb); + if (landmark) + { + cb(landmark); + } + } +} + +void LLItemBridge::doShowOnMap(LLLandmark* landmark) +{ + LLVector3d landmark_global_pos; + // landmark has already been tested for NULL by calling routine + if (landmark->getGlobalPos(landmark_global_pos)) + { + LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); + if (!landmark_global_pos.isExactlyZero() && worldmap_instance) + { + worldmap_instance->trackLocation(landmark_global_pos); + LLFloaterReg::showInstance("world_map", "center"); + } + } +} + +void copy_slurl_to_clipboard_callback_inv(const std::string& slurl) +{ + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); +} + +void LLItemBridge::selectItem() +{ + LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); + if(item && !item->isFinished()) + { + //item->fetchFromServer(); + LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false); + } +} + +void LLItemBridge::restoreItem() +{ + LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); + if(item) + { + LLInventoryModel* model = getInventoryModel(); + bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT); + + const LLUUID new_parent = model->findCategoryUUIDForType(is_snapshot? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType())); + // do not restamp on restore. + LLInvFVBridge::changeItemParent(model, item, new_parent, false); + } +} + +void LLItemBridge::restoreToWorld() +{ + //Similar functionality to the drag and drop rez logic + bool remove_from_inventory = false; + + LLViewerInventoryItem* itemp = static_cast<LLViewerInventoryItem*>(getItem()); + if (itemp) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("RezRestoreToWorld"); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + + msg->nextBlockFast(_PREHASH_InventoryData); + itemp->packMessage(msg); + msg->sendReliable(gAgent.getRegionHost()); + + //remove local inventory copy, sim will deal with permissions and removing the item + //from the actual inventory if its a no-copy etc + if(!itemp->getPermissions().allowCopyBy(gAgent.getID())) + { + remove_from_inventory = true; + } + + // Check if it's in the trash. (again similar to the normal rez logic) + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id)) + { + remove_from_inventory = true; + } + } + + if(remove_from_inventory) + { + gInventory.deleteObject(itemp->getUUID()); + gInventory.notifyObservers(); + } +} + +void LLItemBridge::gotoItem() +{ + LLInventoryObject *obj = getInventoryObject(); + if (obj && obj->getIsLinkType()) + { + show_item_original(obj->getUUID()); + } +} + +LLUIImagePtr LLItemBridge::getIcon() const +{ + LLInventoryObject *obj = getInventoryObject(); + if (obj) + { + return LLInventoryIcon::getIcon(obj->getType(), + LLInventoryType::IT_NONE, + mIsLink); + } + + return LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT); +} + +LLUIImagePtr LLItemBridge::getIconOverlay() const +{ + if (getItem() && getItem()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + +PermissionMask LLItemBridge::getPermissionMask() const +{ + LLViewerInventoryItem* item = getItem(); + PermissionMask perm_mask = 0; + if (item) perm_mask = item->getPermissionMask(); + return perm_mask; +} + +void LLItemBridge::buildDisplayName() const +{ + if(getItem()) + { + mDisplayName.assign(getItem()->getName()); + } + else + { + mDisplayName.assign(LLStringUtil::null); + } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); + + //Name set, so trigger a sort + LLInventorySort sorter = static_cast<LLFolderViewModelInventory&>(mRootViewModel).getSorter(); + if(mParent && !sorter.isByDate()) + { + mParent->requestSort(); + } +} + +LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const +{ + U8 font = LLFontGL::NORMAL; + const LLViewerInventoryItem* item = getItem(); + + if (get_is_item_worn(mUUID)) + { + // LL_INFOS() << "BOLD" << LL_ENDL; + font |= LLFontGL::BOLD; + } + else if(item && item->getIsLinkType()) + { + font |= LLFontGL::ITALIC; + } + + return (LLFontGL::StyleFlags)font; +} + +std::string LLItemBridge::getLabelSuffix() const +{ + // String table is loaded before login screen and inventory items are + // loaded after login, so LLTrans should be ready. + static std::string NO_COPY = LLTrans::getString("no_copy_lbl"); + static std::string NO_MOD = LLTrans::getString("no_modify_lbl"); + static std::string NO_XFER = LLTrans::getString("no_transfer_lbl"); + static std::string LINK = LLTrans::getString("link"); + static std::string BROKEN_LINK = LLTrans::getString("broken_link"); + std::string suffix; + LLInventoryItem* item = getItem(); + if(item) + { + // Any type can have the link suffix... + bool broken_link = LLAssetType::lookupIsLinkType(item->getType()); + if (broken_link) return BROKEN_LINK; + + bool link = item->getIsLinkType(); + if (link) return LINK; + + // ...but it's a bit confusing to put nocopy/nomod/etc suffixes on calling cards. + if(LLAssetType::AT_CALLINGCARD != item->getType() + && item->getPermissions().getOwner() == gAgent.getID()) + { + bool copy = item->getPermissions().allowCopyBy(gAgent.getID()); + if (!copy) + { + suffix += " "; + suffix += NO_COPY; + } + bool mod = item->getPermissions().allowModifyBy(gAgent.getID()); + if (!mod) + { + suffix += suffix.empty() ? " " : ","; + suffix += NO_MOD; + } + bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, + gAgent.getID()); + if (!xfer) + { + suffix += suffix.empty() ? " " : ","; + suffix += NO_XFER; + } + } + } + return suffix; +} + +time_t LLItemBridge::getCreationDate() const +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + return item->getCreationDate(); + } + return 0; +} + + +bool LLItemBridge::isItemRenameable() const +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + // (For now) Don't allow calling card rename since that may confuse users as to + // what the calling card points to. + if (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD) + { + return false; + } + + if (!item->isFinished()) // EXT-8662 + { + return false; + } + + if (isInboxFolder()) + { + return false; + } + + return (item->getPermissions().allowModifyBy(gAgent.getID())); + } + return false; +} + +bool LLItemBridge::renameItem(const std::string& new_name) +{ + if(!isItemRenameable()) + return false; + LLPreview::dirty(mUUID); + LLInventoryModel* model = getInventoryModel(); + if(!model) + return false; + LLViewerInventoryItem* item = getItem(); + if(item && (item->getName() != new_name)) + { + LLSD updates; + updates["name"] = new_name; + update_inventory_item(item->getUUID(),updates, NULL); + } + // return false because we either notified observers (& therefore + // rebuilt) or we didn't update. + return false; +} + +bool LLItemBridge::removeItem() +{ + if(!isItemRemovable()) + { + return false; + } + + // move it to the trash + LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + LLViewerInventoryItem* item = getItem(); + if (!item) return false; + if (item->getType() != LLAssetType::AT_LSL_TEXT) + { + LLPreview::hide(mUUID, true); + } + // Already in trash + if (model->isObjectDescendentOf(mUUID, trash_id)) return false; + + LLNotification::Params params("ConfirmItemDeleteHasLinks"); + params.functor.function(boost::bind(&LLItemBridge::confirmRemoveItem, this, _1, _2)); + + // Check if this item has any links. If generic inventory linking is enabled, + // we can't do this check because we may have items in a folder somewhere that is + // not yet in memory, so we don't want false negatives. (If disabled, then we + // know we only have links in the Outfits folder which we explicitly fetch.) + static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); + if (!inventory_linking) + { + if (!item->getIsLinkType()) + { + LLInventoryModel::item_array_t item_array = gInventory.collectLinksTo(mUUID); + const U32 num_links = item_array.size(); + if (num_links > 0) + { + // Warn if the user is will break any links when deleting this item. + LLNotifications::instance().add(params); + return false; + } + } + } + + LLNotifications::instance().forceResponse(params, 0); + model->checkTrashOverflow(); + return true; +} + +bool LLItemBridge::confirmRemoveItem(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return false; + + LLInventoryModel* model = getInventoryModel(); + if (!model) return false; + + LLViewerInventoryItem* item = getItem(); + if (!item) return false; + + const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + // if item is not already in trash + if(item && !model->isObjectDescendentOf(mUUID, trash_id)) + { + // move to trash, and restamp + LLInvFVBridge::changeItemParent(model, item, trash_id, true); + // delete was successful + return true; + } + return false; +} + +bool LLItemBridge::isItemCopyable(bool can_copy_as_link) const +{ + LLViewerInventoryItem* item = getItem(); + if (!item) + { + return false; + } + // Can't copy worn objects. + // Worn objects are tied to their inworld conterparts + // Copy of modified worn object will return object with obsolete asset and inventory + if (get_is_item_worn(mUUID)) + { + return false; + } + + static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); + return (can_copy_as_link && inventory_linking) + || (mIsLink && inventory_linking) + || item->getPermissions().allowCopyBy(gAgent.getID()); +} + +LLViewerInventoryItem* LLItemBridge::getItem() const +{ + LLViewerInventoryItem* item = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + item = (LLViewerInventoryItem*)model->getItem(mUUID); + } + return item; +} + +const LLUUID& LLItemBridge::getThumbnailUUID() const +{ + LLViewerInventoryItem* item = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + item = (LLViewerInventoryItem*)model->getItem(mUUID); + } + if (item) + { + return item->getThumbnailUUID(); + } + return LLUUID::null; +} + +bool LLItemBridge::isItemPermissive() const +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + return item->getIsFullPerm(); + } + return false; +} + +// +=================================================+ +// | LLFolderBridge | +// +=================================================+ + +LLHandle<LLFolderBridge> LLFolderBridge::sSelf; + +// Can be moved to another folder +bool LLFolderBridge::isItemMovable() const +{ + LLInventoryObject* obj = getInventoryObject(); + if(obj) + { + // If it's a protected type folder, we can't move it + if (LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType())) + return false; + return true; + } + return false; +} + +void LLFolderBridge::selectItem() +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID()); + if (cat) + { + cat->fetch(); + } +} + +void LLFolderBridge::buildDisplayName() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + // *TODO: to be removed when database supports multi language. This is a + // temporary attempt to display the inventory folder in the user locale. + // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID + // it uses the same way to find localized string + + // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) + // Translation of Accessories folder in Library inventory folder + bool accessories = false; + if(getName() == "Accessories") + { + //To ensure that Accessories folder is in Library we have to check its parent folder. + //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model + LLInventoryCategory* cat = gInventory.getCategory(getUUID()); + if(cat) + { + const LLUUID& parent_folder_id = cat->getParentUUID(); + accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); + } + } + + //"Accessories" inventory category has folder type FT_NONE. So, this folder + //can not be detected as protected with LLFolderType::lookupIsProtectedType + mDisplayName.assign(getName()); + if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) + { + LLTrans::findString(mDisplayName, std::string("InvFolder ") + getName(), LLSD()); + } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); + + //Name set, so trigger a sort + LLInventorySort sorter = static_cast<LLFolderViewModelInventory&>(mRootViewModel).getSorter(); + if(mParent && sorter.isFoldersByName()) + { + mParent->requestSort(); + } +} + +std::string LLFolderBridge::getLabelSuffix() const +{ + static LLCachedControl<bool> xui_debug(gSavedSettings, "DebugShowXUINames", 0); + + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= FOLDER_LOADING_MESSAGE_DELAY) + { + return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); + } + std::string suffix = ""; + if (xui_debug) + { + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(getUUID(), cats, items); + + LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID()); + if (cat) + { + LLStringUtil::format_map_t args; + args["[FOLDER_COUNT]"] = llformat("%d", cats->size()); + args["[ITEMS_COUNT]"] = llformat("%d", items->size()); + args["[VERSION]"] = llformat("%d", cat->getVersion()); + args["[VIEWER_DESCENDANT_COUNT]"] = llformat("%d", cats->size() + items->size()); + args["[SERVER_DESCENDANT_COUNT]"] = llformat("%d", cat->getDescendentCount()); + suffix = " " + LLTrans::getString("InventoryFolderDebug", args); + } + } + else if(mShowDescendantsCount) + { + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(getUUID(), cat_array, item_array, true); + S32 count = item_array.size(); + if(count > 0) + { + std::ostringstream oss; + oss << count; + LLStringUtil::format_map_t args; + args["[ITEMS_COUNT]"] = oss.str(); + suffix = " " + LLTrans::getString("InventoryItemsCount", args); + } + } + + return LLInvFVBridge::getLabelSuffix() + suffix; +} + +LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const +{ + return LLFontGL::NORMAL; +} + +const LLUUID& LLFolderBridge::getThumbnailUUID() const +{ + LLViewerInventoryCategory* cat = getCategory(); + if (cat) + { + return cat->getThumbnailUUID(); + } + return LLUUID::null; +} + +void LLFolderBridge::update() +{ + // we know we have children but haven't fetched them (doesn't obey filter) + bool loading = !isUpToDate() && hasChildren() && mFolderViewItem->isOpen(); + + if (loading != mIsLoading) + { + if ( loading ) + { + // Measure how long we've been in the loading state + mTimeSinceRequestStart.reset(); + } + mIsLoading = loading; + + mFolderViewItem->refresh(); + } +} + +// Can be destroyed (or moved to trash) +bool LLFolderBridge::isItemRemovable(bool check_worn) const +{ + if (!get_is_category_and_children_removable(getInventoryModel(), mUUID, check_worn)) + { + return false; + } + + if (isMarketplaceListingsFolder() + && (!LLMarketplaceData::instance().isSLMDataFetched() || LLMarketplaceData::instance().getActivationState(mUUID))) + { + return false; + } + + return true; +} + +bool LLFolderBridge::isUpToDate() const +{ + LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + if( !category ) + { + return false; + } + + return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; +} + +bool LLFolderBridge::isItemCopyable(bool can_copy_as_link) const +{ + if (can_copy_as_link && !LLFolderType::lookupIsProtectedType(getPreferredType())) + { + // Can copy and paste unprotected folders as links + return true; + } + + // Folders are copyable if items in them are, recursively, copyable. + + // Get the content of the folder + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(mUUID,cat_array,item_array); + + // Check the items + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLItemBridge item_br(mInventoryPanel.get(), mRoot, item->getUUID()); + if (!item_br.isItemCopyable(false)) + { + return false; + } + } + + // Check the folders + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* category = *iter; + LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, category->getUUID()); + if (!cat_br.isItemCopyable(false)) + { + return false; + } + } + + return true; +} + +bool LLFolderBridge::isClipboardPasteable() const +{ + if ( ! LLInvFVBridge::isClipboardPasteable() ) + return false; + + // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599 + if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) ) + { + LLInventoryModel* model = getInventoryModel(); + if ( !model ) + { + return false; + } + + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + const LLViewerInventoryCategory *current_cat = getCategory(); + + // Search for the direct descendent of current Friends subfolder among all pasted items, + // and return false if is found. + for(S32 i = objects.size() - 1; i >= 0; --i) + { + const LLUUID &obj_id = objects.at(i); + if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) ) + { + return false; + } + } + + } + return true; +} + +bool LLFolderBridge::isClipboardPasteableAsLink() const +{ + // Check normal paste-as-link permissions + if (!LLInvFVBridge::isClipboardPasteableAsLink()) + { + return false; + } + + const LLInventoryModel* model = getInventoryModel(); + if (!model) + { + return false; + } + + const LLViewerInventoryCategory *current_cat = getCategory(); + if (current_cat) + { + const bool is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat ); + const LLUUID ¤t_cat_id = current_cat->getUUID(); + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + S32 count = objects.size(); + for(S32 i = 0; i < count; i++) + { + const LLUUID &obj_id = objects.at(i); + const LLInventoryCategory *cat = model->getCategory(obj_id); + if (cat) + { + const LLUUID &cat_id = cat->getUUID(); + // Don't allow recursive pasting + if ((cat_id == current_cat_id) || + model->isObjectDescendentOf(current_cat_id, cat_id)) + { + return false; + } + } + // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599 + if ( is_in_friend_folder ) + { + // If object is direct descendent of current Friends subfolder than return false. + // Note: We can't use 'const LLInventoryCategory *cat', because it may be null + // in case type of obj_id is LLInventoryItem. + if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) ) + { + return false; + } + } + } + } + return true; + +} + + +bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, + bool drop, + std::string& tooltip_msg, + bool is_link, + bool user_confirm, + LLPointer<LLInventoryCallback> cb) +{ + + LLInventoryModel* model = getInventoryModel(); + + if (!inv_cat) return false; // shouldn't happen, but in case item is incorrectly parented in which case inv_cat will be NULL + if (!model) return false; + if (!isAgentAvatarValid()) return false; + if (!isAgentInventory()) return false; // cannot drag categories into library + + LLInventoryPanel* destination_panel = mInventoryPanel.get(); + if (!destination_panel) return false; + + LLInventoryFilter* filter = getInventoryFilter(); + if (!filter) return false; + + const LLUUID &cat_id = inv_cat->getUUID(); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID from_folder_uuid = inv_cat->getParentUUID(); + + const bool move_is_into_current_outfit = (mUUID == current_outfit_id); + const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const bool move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id); + + // check to make sure source is agent inventory, and is represented there. + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + const bool is_agent_inventory = (model->getCategory(cat_id) != NULL) + && (LLToolDragAndDrop::SOURCE_AGENT == source); + + bool accept = false; + U64 filter_types = filter->getFilterTypes(); + bool use_filter = filter_types && (filter_types&LLInventoryFilter::FILTERTYPE_DATE || (filter_types&LLInventoryFilter::FILTERTYPE_OBJECT)==0); + + if (is_agent_inventory) + { + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + + const bool move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); + const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const bool move_is_into_current_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_CURRENT_OUTFIT); + const bool move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); + const bool move_is_into_lost_and_found = model->isObjectDescendentOf(mUUID, lost_and_found_id); + + //-------------------------------------------------------------------------------- + // Determine if folder can be moved. + // + + bool is_movable = true; + + if (is_movable && (marketplacelistings_id == cat_id)) + { + is_movable = false; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot"); + } + if (is_movable && move_is_from_marketplacelistings && LLMarketplaceData::instance().getActivationState(cat_id)) + { + // If the incoming folder is listed and active (and is therefore either the listing or the version folder), + // then moving is *not* allowed + is_movable = false; + tooltip_msg = LLTrans::getString("TooltipOutboxDragActive"); + } + if (is_movable && (mUUID == cat_id)) + { + is_movable = false; + tooltip_msg = LLTrans::getString("TooltipDragOntoSelf"); + } + if (is_movable && (model->isObjectDescendentOf(mUUID, cat_id))) + { + is_movable = false; + tooltip_msg = LLTrans::getString("TooltipDragOntoOwnChild"); + } + if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + is_movable = false; + // tooltip? + } + + U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit"); + if (is_movable && move_is_into_outfit) + { + if (mUUID == my_outifts_id) + { + if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutfitNotInInventory"); + is_movable = false; + } + else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear)) + { + is_movable = true; + } + else + { + tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit"); + is_movable = false; + } + } + else if(getCategory() && getCategory()->getPreferredType() == LLFolderType::FT_NONE) + { + is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)); + } + else + { + is_movable = false; + } + } + if(is_movable && move_is_into_current_outfit && is_link) + { + is_movable = false; + } + if (is_movable && move_is_into_lost_and_found) + { + is_movable = false; + } + if (is_movable && (mUUID == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE))) + { + is_movable = false; + // tooltip? + } + if (is_movable && (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)) + { + // One cannot move a folder into a stock folder + is_movable = false; + // tooltip? + } + + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + if (is_movable) + { + model->collectDescendents(cat_id, descendent_categories, descendent_items, false); + for (S32 i=0; i < descendent_categories.size(); ++i) + { + LLInventoryCategory* category = descendent_categories[i]; + if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) + { + // Can't move "special folders" (e.g. Textures Folder). + is_movable = false; + break; + } + } + } + if (is_movable + && move_is_into_current_outfit + && descendent_items.size() > max_items_to_wear) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + not_worn); + + if (items.size() > max_items_to_wear) + { + // Can't move 'large' folders into current outfit: MAINT-4086 + is_movable = false; + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", max_items_to_wear); + tooltip_msg = LLTrans::getString("TooltipTooManyWearables",args); + } + } + if (is_movable && move_is_into_trash) + { + for (S32 i=0; i < descendent_items.size(); ++i) + { + LLInventoryItem* item = descendent_items[i]; + if (get_is_item_worn(item->getUUID())) + { + is_movable = false; + break; // It's generally movable, but not into the trash. + } + } + } + if (is_movable && move_is_into_landmarks) + { + for (S32 i=0; i < descendent_items.size(); ++i) + { + LLViewerInventoryItem* item = descendent_items[i]; + + // Don't move anything except landmarks and categories into Landmarks folder. + // We use getType() instead of getActua;Type() to allow links to landmarks and folders. + if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType()) + { + is_movable = false; + break; // It's generally movable, but not into Landmarks. + } + } + } + + if (is_movable && move_is_into_marketplacelistings) + { + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); + LLViewerInventoryCategory * dest_folder = getCategory(); + S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount()); + is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size); + } + + if (is_movable && !move_is_into_landmarks) + { + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(false); + is_movable = active_panel != NULL; + + // For a folder to pass the filter all its descendants are required to pass. + // We make this exception to allow reordering folders within an inventory panel, + // which has a filter applied, like Recent tab for example. + // There may be folders which are displayed because some of their descendants pass + // the filter, but other don't, and thus remain hidden. Without this check, + // such folders would not be allowed to be moved within a panel. + if (destination_panel == active_panel) + { + is_movable = true; + } + else + { + LLFolderView* active_folder_view = NULL; + + if (is_movable) + { + active_folder_view = active_panel->getRootFolder(); + is_movable = active_folder_view != NULL; + } + + if (is_movable && use_filter) + { + // Check whether the folder being dragged from active inventory panel + // passes the filter of the destination panel. + is_movable = check_category(model, cat_id, active_panel, filter); + } + } + } + // + //-------------------------------------------------------------------------------- + + accept = is_movable; + + if (accept && drop) + { + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + if (move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(cat_id) || + LLMarketplaceData::instance().isListedAndActive(cat_id))) + { + if (LLMarketplaceData::instance().isListed(cat_id) || LLMarketplaceData::instance().isVersionFolder(cat_id)) + { + // Move the active version folder or listing folder itself outside marketplace listings will unlist the listing so ask that question specifically + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + } + else + { + // Any other case will simply modify but not unlist an active listed listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + } + return true; + } + if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isVersionFolder(cat_id)) + { + // Moving the version folder from its location will deactivate it. Ask confirmation. + LLNotificationsUtil::add("ConfirmMerchantClearVersion", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID)) + { + // Moving something in an active listed listing will modify it. Ask confirmation. + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isListed(cat_id)) + { + // Moving a whole listing folder will result in archival of SLM data. Ask confirmation. + LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings) + { + LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + } + // Look for any gestures and deactivate them + if (move_is_into_trash) + { + for (S32 i=0; i < descendent_items.size(); i++) + { + LLInventoryItem* item = descendent_items[i]; + if (item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(item->getUUID())) + { + LLGestureMgr::instance().deactivateGesture(item->getUUID()); + } + } + } + + if (mUUID == my_outifts_id) + { + // Category can contains objects, + // create a new folder and populate it with links to original objects + dropToMyOutfits(inv_cat, cb); + } + // if target is current outfit folder we use link + else if (move_is_into_current_outfit && + (inv_cat->getPreferredType() == LLFolderType::FT_NONE || + inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)) + { + // traverse category and add all contents to currently worn. + bool append = true; + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); + if (cb) cb->fire(inv_cat->getUUID()); + } + else if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(inv_cat, mUUID); + if (cb) cb->fire(inv_cat->getUUID()); + } + else + { + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX))) + { + set_dad_inbox_object(cat_id); + } + + // Reparent the folder and restamp children if it's moving + // into trash. + LLInvFVBridge::changeCategoryParent( + model, + (LLViewerInventoryCategory*)inv_cat, + mUUID, + move_is_into_trash); + if (cb) cb->fire(inv_cat->getUUID()); + } + if (move_is_from_marketplacelistings) + { + // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder) + if (from_folder_uuid == marketplacelistings_id) + { + // Clear the folder from the marketplace in case it is a listing folder + if (LLMarketplaceData::instance().isListed(cat_id)) + { + LLMarketplaceData::instance().clearListing(cat_id); + } + } + else + { + // If we move from within an active (listed) listing, checks that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) + { + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + } + ); + } + // In all cases, update the listing we moved from so suffix are updated + update_marketplace_category(from_folder_uuid); + if (cb) cb->fire(inv_cat->getUUID()); + } + } + } + } + else if (LLToolDragAndDrop::SOURCE_WORLD == source) + { + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = false; + } + else + { + // Todo: fix me. moving from task inventory doesn't have a completion callback, + // yet making a copy creates new item id so this doesn't work right + std::function<void(S32, void*, const LLMoveInv*)> callback = [cb](S32, void*, const LLMoveInv* move_inv) mutable + { + two_uuids_list_t::const_iterator move_it; + for (move_it = move_inv->mMoveList.begin(); + move_it != move_inv->mMoveList.end(); + ++move_it) + { + if (cb) + { + cb->fire(move_it->second); + } + } + }; + accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, callback, NULL, filter); + } + } + else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = false; + } + else + { + // Accept folders that contain complete outfits. + accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id); + } + + if (accept && drop) + { + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false); + } + } + + return accept; +} + +void warn_move_inventory(LLViewerObject* object, std::shared_ptr<LLMoveInv> move_inv) +{ + const char* dialog = NULL; + if (object->flagScripted()) + { + dialog = "MoveInventoryFromScriptedObject"; + } + else + { + dialog = "MoveInventoryFromObject"; + } + + static LLNotificationPtr notification_ptr; + static std::shared_ptr<LLMoveInv> inv_ptr; + + // Notification blocks user from interacting with inventories so everything that comes after first message + // is part of this message - don'r show it again + // Note: workaround for MAINT-5495 untill proper refactoring and warning system for Drag&Drop can be made. + if (notification_ptr == NULL + || !notification_ptr->isActive() + || LLNotificationsUtil::find(notification_ptr->getID()) == NULL + || inv_ptr->mCategoryID != move_inv->mCategoryID + || inv_ptr->mObjectID != move_inv->mObjectID) + { + notification_ptr = LLNotificationsUtil::add(dialog, LLSD(), LLSD(), boost::bind(move_task_inventory_callback, _1, _2, move_inv)); + inv_ptr = move_inv; + } + else + { + // Notification is alive and not responded, operating inv_ptr should be safe so attach new data + two_uuids_list_t::iterator move_it; + for (move_it = move_inv->mMoveList.begin(); + move_it != move_inv->mMoveList.end(); + ++move_it) + { + inv_ptr->mMoveList.push_back(*move_it); + } + move_inv.reset(); + } +} + +// Move/copy all inventory items from the Contents folder of an in-world +// object to the agent's inventory, inside a given category. +bool move_inv_category_world_to_agent(const LLUUID& object_id, + const LLUUID& category_id, + bool drop, + std::function<void(S32, void*, const LLMoveInv*)> callback, + void* user_data, + LLInventoryFilter* filter) +{ + // Make sure the object exists. If we allowed dragging from + // anonymous objects, it would be possible to bypass + // permissions. + // content category has same ID as object itself + LLViewerObject* object = gObjectList.findObject(object_id); + if(!object) + { + LL_INFOS() << "Object not found for drop." << LL_ENDL; + return false; + } + + // this folder is coming from an object, as there is only one folder in an object, the root, + // we need to collect the entire contents and handle them as a group + LLInventoryObject::object_list_t inventory_objects; + object->getInventoryContents(inventory_objects); + + if (inventory_objects.empty()) + { + LL_INFOS() << "Object contents not found for drop." << LL_ENDL; + return false; + } + + bool accept = false; + bool is_move = false; + bool use_filter = false; + if (filter) + { + U64 filter_types = filter->getFilterTypes(); + use_filter = filter_types && (filter_types&LLInventoryFilter::FILTERTYPE_DATE || (filter_types&LLInventoryFilter::FILTERTYPE_OBJECT)==0); + } + + // coming from a task. Need to figure out if the person can + // move/copy this item. + LLInventoryObject::object_list_t::iterator it = inventory_objects.begin(); + LLInventoryObject::object_list_t::iterator end = inventory_objects.end(); + for ( ; it != end; ++it) + { + LLInventoryItem* item = dynamic_cast<LLInventoryItem*>(it->get()); + if (!item) + { + LL_WARNS() << "Invalid inventory item for drop" << LL_ENDL; + continue; + } + + // coming from a task. Need to figure out if the person can + // move/copy this item. + LLPermissions perm(item->getPermissions()); + if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) + && perm.allowTransferTo(gAgent.getID()))) +// || gAgent.isGodlike()) + { + accept = true; + } + else if(object->permYouOwner()) + { + // If the object cannot be copied, but the object the + // inventory is owned by the agent, then the item can be + // moved from the task to agent inventory. + is_move = true; + accept = true; + } + + if (accept && use_filter) + { + accept = filter->check(item); + } + + if (!accept) + { + break; + } + } + + if(drop && accept) + { + it = inventory_objects.begin(); + std::shared_ptr<LLMoveInv> move_inv(new LLMoveInv); + move_inv->mObjectID = object_id; + move_inv->mCategoryID = category_id; + move_inv->mCallback = callback; + move_inv->mUserData = user_data; + + for ( ; it != end; ++it) + { + two_uuids_t two(category_id, (*it)->getUUID()); + move_inv->mMoveList.push_back(two); + } + + if(is_move) + { + // Callback called from within here. + warn_move_inventory(object, move_inv); + } + else + { + LLNotification::Params params("MoveInventoryFromObject"); + params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); + LLNotifications::instance().forceResponse(params, 0); + } + } + return accept; +} + +void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) +{ + // Bail out immediately if no descendents + if( mComplete.empty() ) + { + LL_WARNS() << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << LL_ENDL; + if (clear_observer) + { + gInventory.removeObserver(this); + delete this; + } + return; + } + + // Copy the list of complete fetched folders while "this" is still valid + uuid_vec_t completed_folder = mComplete; + + // Clean up, and remove this as an observer now since recursive calls + // could notify observers and throw us into an infinite loop. + if (clear_observer) + { + gInventory.removeObserver(this); + delete this; + } + + for (uuid_vec_t::iterator current_folder = completed_folder.begin(); current_folder != completed_folder.end(); ++current_folder) + { + // Get the information on the fetched folder items and subfolders and fetch those + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array); + + S32 item_count(0); + if( item_array ) + { + item_count = item_array->size(); + } + + S32 cat_count(0); + if( cat_array ) + { + cat_count = cat_array->size(); + } + + // Move to next if current folder empty + if ((item_count == 0) && (cat_count == 0)) + { + continue; + } + + uuid_vec_t ids; + LLRightClickInventoryFetchObserver* outfit = NULL; + LLRightClickInventoryFetchDescendentsObserver* categories = NULL; + + // Fetch the items + if (item_count) + { + for (S32 i = 0; i < item_count; ++i) + { + ids.push_back(item_array->at(i)->getUUID()); + } + outfit = new LLRightClickInventoryFetchObserver(ids); + } + // Fetch the subfolders + if (cat_count) + { + for (S32 i = 0; i < cat_count; ++i) + { + ids.push_back(cat_array->at(i)->getUUID()); + } + categories = new LLRightClickInventoryFetchDescendentsObserver(ids); + } + + // Perform the item fetch + if (outfit) + { + outfit->startFetch(); + outfit->execute(); // Not interested in waiting and this will be right 99% of the time. + delete outfit; +//Uncomment the following code for laggy Inventory UI. + /* + if (outfit->isFinished()) + { + // everything is already here - call done. + outfit->execute(); + delete outfit; + } + else + { + // it's all on its way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(outfit); + } + */ + } + // Perform the subfolders fetch : this is where we truly recurse down the folder hierarchy + if (categories) + { + categories->startFetch(); + if (categories->isFinished()) + { + // everything is already here - call done. + categories->execute(); + delete categories; + } + else + { + // it's all on its way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(categories); + } + } + } +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryWearObserver +// +// Observer for "copy and wear" operation to support knowing +// when the all of the contents have been added to inventory. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryCopyAndWearObserver : public LLInventoryObserver +{ +public: + LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count, bool folder_added=false, bool replace=false) : + mCatID(cat_id), mContentsCount(count), mFolderAdded(folder_added), mReplace(replace){} + virtual ~LLInventoryCopyAndWearObserver() {} + virtual void changed(U32 mask); + +protected: + LLUUID mCatID; + int mContentsCount; + bool mFolderAdded; + bool mReplace; +}; + + + +void LLInventoryCopyAndWearObserver::changed(U32 mask) +{ + if((mask & (LLInventoryObserver::ADD)) != 0) + { + if (!mFolderAdded) + { + const std::set<LLUUID>& changed_items = gInventory.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) + { + if ((*id_it) == mCatID) + { + mFolderAdded = true; + break; + } + } + } + + if (mFolderAdded) + { + LLViewerInventoryCategory* category = gInventory.getCategory(mCatID); + if (NULL == category) + { + LL_WARNS() << "gInventory.getCategory(" << mCatID + << ") was NULL" << LL_ENDL; + } + else + { + if (category->getDescendentCount() == + mContentsCount) + { + gInventory.removeObserver(this); + LLAppearanceMgr::instance().wearInventoryCategory(category, false, !mReplace); + delete this; + } + } + } + + } +} + + + +void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("open" == action) + { + LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mInventoryPanel.get()->getItemByID(mUUID)); + if (f) + { + f->toggleOpen(); + } + + return; + } + else if ("thumbnail" == action) + { + LLSD data(mUUID); + LLFloaterReg::showInstance("change_item_thumbnail", data); + return; + } + else if ("paste" == action) + { + pasteFromClipboard(); + return; + } + else if ("paste_link" == action) + { + pasteLinkFromClipboard(); + return; + } + else if ("properties" == action) + { + showProperties(); + return; + } + else if ("replaceoutfit" == action) + { + modifyOutfit(false); + return; + } + else if ("addtooutfit" == action) + { + modifyOutfit(true); + return; + } + else if ("show_in_main_panel" == action) + { + LLInventoryPanel::openInventoryPanelAndSetSelection(true, mUUID, true); + return; + } + else if ("cut" == action) + { + cutToClipboard(); + return; + } + else if ("copy" == action) + { + copyToClipboard(); + return; + } + else if ("removefromoutfit" == action) + { + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + LLViewerInventoryCategory* cat = getCategory(); + if(!cat) return; + + LLAppearanceMgr::instance().takeOffOutfit( cat->getLinkedUUID() ); + return; + } + else if ("copyoutfittoclipboard" == action) + { + copyOutfitToClipboard(); + } + else if ("purge" == action) + { + purgeItem(model, mUUID); + return; + } + else if ("restore" == action) + { + restoreItem(); + return; + } + else if ("marketplace_list" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 1) + { + LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID); + mMessage = ""; + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [this](bool result) + { + // todo: might need to ensure bridge/mUUID exists or this will cause crashes + if (!result) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().activateListing(mUUID, true); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3) + ); + } + return; + } + else if ("marketplace_activate" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 2) + { + mMessage = ""; + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) + { + if (!result) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); + } + else + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + false, + 2); + } + return; + } + else if ("marketplace_unlist" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 1) + { + LLMarketplaceData::instance().activateListing(mUUID,false,1); + } + return; + } + else if ("marketplace_deactivate" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 2) + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), LLUUID::null, 1); + } + return; + } + else if ("marketplace_create_listing" == action) + { + mMessage = ""; + + // first run vithout fix_hierarchy, second run with fix_hierarchy + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) + { + if (!result) + { + mMessage = ""; + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) + { + if (result) + { + LLNotificationsUtil::add("MerchantForceValidateListing"); + LLMarketplaceData::instance().createListing(mUUID); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + true); + } + else + { + LLMarketplaceData::instance().createListing(mUUID); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + false); + + return; + } + else if ("marketplace_disassociate_listing" == action) + { + LLMarketplaceData::instance().clearListing(mUUID); + return; + } + else if ("marketplace_get_listing" == action) + { + // This is used only to exercise the SLM API but won't be shown to end users + LLMarketplaceData::instance().getListing(mUUID); + return; + } + else if ("marketplace_associate_listing" == action) + { + LLFloaterAssociateListing::show(mUUID); + return; + } + else if ("marketplace_check_listing" == action) + { + LLSD data(mUUID); + LLFloaterReg::showInstance("marketplace_validation", data); + return; + } + else if ("marketplace_edit_listing" == action) + { + std::string url = LLMarketplaceData::instance().getListingURL(mUUID); + if (!url.empty()) + { + LLUrlAction::openURL(url); + } + return; + } +#ifndef LL_RELEASE_FOR_DOWNLOAD + else if ("delete_system_folder" == action) + { + removeSystemFolder(); + } +#endif + else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) + { + LLInventoryCategory * cat = gInventory.getCategory(mUUID); + if (!cat) return; + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action))); + } +} + +void LLFolderBridge::gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level) +{ + if (log_level >= LLError::LEVEL_ERROR) + { + if (!mMessage.empty()) + { + // Currently, we do not gather all messages as it creates very long alerts + // Users can get to the whole list of errors on a listing using the "Check for Errors" audit button or "Check listing" right click menu + //mMessage += "\n"; + return; + } + // Take the leading spaces out... + std::string::size_type start = message.find_first_not_of(" "); + // Append the message + mMessage += message.substr(start, message.length() - start); + } +} + +void LLFolderBridge::copyOutfitToClipboard() +{ + std::string text; + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array); + + S32 item_count(0); + if( item_array ) + { + item_count = item_array->size(); + } + + if (item_count) + { + for (S32 i = 0; i < item_count;) + { + LLSD uuid =item_array->at(i)->getUUID(); + LLViewerInventoryItem* item = gInventory.getItem(uuid); + + i++; + if (item != NULL) + { + // Append a newline to all but the last line + text += i != item_count ? item->getName() + "\n" : item->getName(); + } + } + } + + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(text),0,text.size()); +} + +void LLFolderBridge::openItem() +{ + LL_DEBUGS() << "LLFolderBridge::openItem()" << LL_ENDL; + + LLInventoryPanel* panel = mInventoryPanel.get(); + if (!panel) + { + return; + } + LLInventoryModel* model = getInventoryModel(); + if (!model) + { + return; + } + if (mUUID.isNull()) + { + return; + } + panel->onFolderOpening(mUUID); + bool fetching_inventory = model->fetchDescendentsOf(mUUID); + // Only change folder type if we have the folder contents. + if (!fetching_inventory) + { + // Disabling this for now, it's causing crash when new items are added to folders + // since folder type may change before new item item has finished processing. + // determineFolderType(); + } +} + +void LLFolderBridge::closeItem() +{ + determineFolderType(); +} + +void LLFolderBridge::determineFolderType() +{ + if (isUpToDate()) + { + LLInventoryModel* model = getInventoryModel(); + LLViewerInventoryCategory* category = model->getCategory(mUUID); + if (category) + { + category->determineFolderType(); + } + } +} + +bool LLFolderBridge::isItemRenameable() const +{ + return get_is_category_renameable(getInventoryModel(), mUUID); +} + +void LLFolderBridge::restoreItem() +{ + LLViewerInventoryCategory* cat; + cat = (LLViewerInventoryCategory*)getCategory(); + if(cat) + { + LLInventoryModel* model = getInventoryModel(); + const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType())); + // do not restamp children on restore + LLInvFVBridge::changeCategoryParent(model, cat, new_parent, false); + } +} + +LLFolderType::EType LLFolderBridge::getPreferredType() const +{ + LLFolderType::EType preferred_type = LLFolderType::FT_NONE; + LLViewerInventoryCategory* cat = getCategory(); + if(cat) + { + preferred_type = cat->getPreferredType(); + } + + return preferred_type; +} + +// Icons for folders are based on the preferred type +LLUIImagePtr LLFolderBridge::getIcon() const +{ + return getFolderIcon(false); +} + +LLUIImagePtr LLFolderBridge::getIconOpen() const +{ + return getFolderIcon(true); +} + +LLUIImagePtr LLFolderBridge::getFolderIcon(bool is_open) const +{ + LLFolderType::EType preferred_type = getPreferredType(); + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open)); +} + +// static : use by LLLinkFolderBridge to get the closed type icons +LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) +{ + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, false)); +} + +LLUIImagePtr LLFolderBridge::getIconOverlay() const +{ + if (getInventoryObject() && getInventoryObject()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + +bool LLFolderBridge::renameItem(const std::string& new_name) +{ + + LLScrollOnRenameObserver *observer = new LLScrollOnRenameObserver(mUUID, mRoot); + gInventory.addObserver(observer); + + rename_category(getInventoryModel(), mUUID, new_name); + + // return false because we either notified observers (& therefore + // rebuilt) or we didn't update. + return false; +} + +bool LLFolderBridge::removeItem() +{ + if(!isItemRemovable()) + { + return false; + } + const LLViewerInventoryCategory *cat = getCategory(); + + LLSD payload; + LLSD args; + args["FOLDERNAME"] = cat->getName(); + + LLNotification::Params params("ConfirmDeleteProtectedCategory"); + params.payload(payload).substitutions(args).functor.function(boost::bind(&LLFolderBridge::removeItemResponse, this, _1, _2)); + LLNotifications::instance().forceResponse(params, 0); + return true; +} + + +bool LLFolderBridge::removeSystemFolder() +{ + const LLViewerInventoryCategory *cat = getCategory(); + if (!LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + { + return false; + } + + LLSD payload; + LLSD args; + args["FOLDERNAME"] = cat->getName(); + + LLNotification::Params params("ConfirmDeleteProtectedCategory"); + params.payload(payload).substitutions(args).functor.function(boost::bind(&LLFolderBridge::removeItemResponse, this, _1, _2)); + { + LLNotifications::instance().add(params); + } + return true; +} + +bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + + // if they choose delete, do it. Otherwise, don't do anything + if(option == 0) + { + // move it to the trash + LLPreview::hide(mUUID); + getInventoryModel()->removeCategory(mUUID); + return true; + } + return false; +} + +//Recursively update the folder's creation date +void LLFolderBridge::updateHierarchyCreationDate(time_t date) +{ + if(getCreationDate() < date) + { + setCreationDate(date); + if(mParent) + { + static_cast<LLFolderBridge *>(mParent)->updateHierarchyCreationDate(date); + } + } +} + +void LLFolderBridge::pasteFromClipboard() +{ + LLInventoryModel* model = getInventoryModel(); + if (model && isClipboardPasteable()) + { + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const bool paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + + bool cut_from_marketplacelistings = false; + if (LLClipboard::instance().isCutMode()) + { + //Items are not removed from folder on "cut", so we need update listing folder on "paste" operation + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + if(gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) || + LLMarketplaceData::instance().isListedAndActive(item_id))) + { + cut_from_marketplacelistings = true; + break; + } + } + } + if (cut_from_marketplacelistings || (paste_into_marketplacelistings && !LLMarketplaceData::instance().isListed(mUUID) && LLMarketplaceData::instance().isInActiveFolder(mUUID))) + { + // Prompt the user if pasting in a marketplace active version listing (note that pasting right under the listing folder root doesn't need a prompt) + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_pasteFromClipboard, this, _1, _2)); + } + else + { + // Otherwise just do the paste + perform_pasteFromClipboard(); + } + } +} + +// Callback for pasteFromClipboard if DAMA required... +void LLFolderBridge::callback_pasteFromClipboard(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + std::vector<LLUUID> objects; + std::set<LLUUID> parent_folders; + LLClipboard::instance().pasteFromClipboard(objects); + for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLInventoryObject* obj = gInventory.getObject(*iter); + parent_folders.insert(obj->getParentUUID()); + } + perform_pasteFromClipboard(); + for (std::set<LLUUID>::const_iterator iter = parent_folders.begin(); iter != parent_folders.end(); ++iter) + { + gInventory.addChangedMask(LLInventoryObserver::STRUCTURE, *iter); + } + + } +} + +void LLFolderBridge::perform_pasteFromClipboard() +{ + LLInventoryModel* model = getInventoryModel(); + if (model && isClipboardPasteable()) + { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + + const bool move_is_into_current_outfit = (mUUID == current_outfit_id); + const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const bool move_is_into_favorites = (mUUID == favorites_id); + const bool move_is_into_lost_and_found = model->isObjectDescendentOf(mUUID, lost_and_found_id); + + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + + LLPointer<LLInventoryCallback> cb = NULL; + LLInventoryPanel* panel = mInventoryPanel.get(); + if (panel->getRootFolder()->isSingleFolderMode() && panel->getRootFolderID() == mUUID) + { + cb = new LLPasteIntoFolderCallback(mInventoryPanel); + } + + LLViewerInventoryCategory * dest_folder = getCategory(); + if (move_is_into_marketplacelistings) + { + std::string error_msg; + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); + int index = 0; + for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + LLInventoryCategory *cat = model->getCategory(item_id); + + if (item && !can_move_item_to_marketplace(master_folder, dest_folder, item, error_msg, objects.size() - index, true)) + { + break; + } + if (cat && !can_move_folder_to_marketplace(master_folder, dest_folder, cat, error_msg, objects.size() - index, true, true)) + { + break; + } + ++index; + } + if (!error_msg.empty()) + { + LLSD subs; + subs["[ERROR_CODE]"] = error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + } + else + { + // Check that all items can be moved into that folder : for the moment, only stock folder mismatch is checked + for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + LLInventoryCategory *cat = model->getCategory(item_id); + + if ((item && !dest_folder->acceptItem(item)) || (cat && (dest_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK))) + { + std::string error_msg = LLTrans::getString("TooltipOutboxMixedStock"); + LLSD subs; + subs["[ERROR_CODE]"] = error_msg; + LLNotificationsUtil::add("StockPasteFailed", subs); + return; + } + } + } + + const LLUUID parent_id(mUUID); + + for (std::vector<LLUUID>::const_iterator iter = objects.begin(); + iter != objects.end(); + ++iter) + { + const LLUUID& item_id = (*iter); + + LLInventoryItem *item = model->getItem(item_id); + LLInventoryObject *obj = model->getObject(item_id); + if (obj) + { + + if (move_is_into_lost_and_found) + { + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + return; + } + } + if (move_is_into_outfit) + { + if (!move_is_into_my_outfits && item && can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit, cb); + } + else if (move_is_into_my_outfits && LLAssetType::AT_CATEGORY == obj->getType()) + { + LLInventoryCategory* cat = model->getCategory(item_id); + U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit"); + if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear)) + { + dropToMyOutfits(cat, cb); + } + else + { + LLNotificationsUtil::add("MyOutfitsPasteFailed"); + } + } + else + { + LLNotificationsUtil::add("MyOutfitsPasteFailed"); + } + } + else if (move_is_into_current_outfit) + { + if (item && can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit, cb); + } + else + { + LLNotificationsUtil::add("MyOutfitsPasteFailed"); + } + } + else if (move_is_into_favorites) + { + if (item && can_move_to_landmarks(item)) + { + if (LLClipboard::instance().isCutMode()) + { + LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item); + llassert(viitem); + if (viitem) + { + //changeItemParent() implicity calls dirtyFilter + changeItemParent(model, viitem, parent_id, false); + if (cb) cb->fire(item_id); + } + } + else + { + dropToFavorites(item, cb); + } + } + } + else if (LLClipboard::instance().isCutMode()) + { + // Do a move to "paste" a "cut" + // move_inventory_item() is not enough, as we have to update inventory locally too + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + llassert(vicat); + if (vicat) + { + // Clear the cut folder from the marketplace if it is a listing folder + if (LLMarketplaceData::instance().isListed(item_id)) + { + LLMarketplaceData::instance().clearListing(item_id); + } + if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(vicat, parent_id); + } + else + { + //changeCategoryParent() implicity calls dirtyFilter + changeCategoryParent(model, vicat, parent_id, false); + } + if (cb) cb->fire(item_id); + } + } + else + { + LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item); + llassert(viitem); + if (viitem) + { + if (move_is_into_marketplacelistings) + { + if (!move_item_to_marketplacelistings(viitem, parent_id)) + { + // Stop pasting into the marketplace as soon as we get an error + break; + } + } + else + { + //changeItemParent() implicity calls dirtyFilter + changeItemParent(model, viitem, parent_id, false); + } + if (cb) cb->fire(item_id); + } + } + } + else + { + // Do a "copy" to "paste" a regular copy clipboard + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + llassert(vicat); + if (vicat) + { + if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(vicat, parent_id, true); + } + else + { + copy_inventory_category(model, vicat, parent_id); + } + if (cb) cb->fire(item_id); + } + } + else + { + LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item); + llassert(viitem); + if (viitem) + { + if (move_is_into_marketplacelistings) + { + if (!move_item_to_marketplacelistings(viitem, parent_id, true)) + { + // Stop pasting into the marketplace as soon as we get an error + break; + } + if (cb) cb->fire(item_id); + } + else if (item->getIsLinkType()) + { + link_inventory_object(parent_id, + item_id, + cb); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + cb); + } + } + } + } + } + } + // Change mode to paste for next paste + LLClipboard::instance().setCutMode(false); + } +} + +void LLFolderBridge::pasteLinkFromClipboard() +{ + LLInventoryModel* model = getInventoryModel(); + if(model) + { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + const bool move_is_into_current_outfit = (mUUID == current_outfit_id); + const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + + if (move_is_into_marketplacelistings) + { + // Notify user of failure somehow -- play error sound? modal dialog? + return; + } + + const LLUUID parent_id(mUUID); + + std::vector<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + + LLPointer<LLInventoryCallback> cb = NULL; + LLInventoryPanel* panel = mInventoryPanel.get(); + if (panel->getRootFolder()->isSingleFolderMode()) + { + cb = new LLPasteIntoFolderCallback(mInventoryPanel); + } + + for (std::vector<LLUUID>::const_iterator iter = objects.begin(); + iter != objects.end(); + ++iter) + { + const LLUUID &object_id = (*iter); + if (move_is_into_current_outfit || move_is_into_outfit) + { + LLInventoryItem *item = model->getItem(object_id); + if (item && can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit, cb); + } + } + else if (LLConstPointer<LLInventoryObject> obj = model->getObject(object_id)) + { + link_inventory_object(parent_id, obj, cb); + } + } + // Change mode to paste for next paste + LLClipboard::instance().setCutMode(false); + } +} + +void LLFolderBridge::staticFolderOptionsMenu() +{ + LLFolderBridge* selfp = sSelf.get(); + + if (selfp && selfp->mRoot) + { + selfp->mRoot->updateMenu(); + } +} + +bool LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + model->collectDescendentsIf(mUUID, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_type); + return !item_array.empty(); +} + +void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) +{ + LLInventoryModel* model = getInventoryModel(); + llassert(model != NULL); + + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + const LLUUID &favorites = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + if (outfits_id == mUUID) + { + items.push_back(std::string("New Outfit")); + } + + if (lost_and_found_id == mUUID) + { + // This is the lost+found folder. + items.push_back(std::string("Empty Lost And Found")); + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array); + // Enable Empty menu item only when there is something to act upon. + if (0 == cat_array->size() && 0 == item_array->size()) + { + disabled_items.push_back(std::string("Empty Lost And Found")); + } + + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("upload_def")); + disabled_items.push_back(std::string("create_new")); + } + if (favorites == mUUID) + { + disabled_items.push_back(std::string("New Folder")); + } + if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + if (LLMarketplaceData::instance().isUpdating(mUUID)) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("Rename")); + disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("Copy")); + disabled_items.push_back(std::string("Paste")); + disabled_items.push_back(std::string("Delete")); + } + } + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("upload_def")); + disabled_items.push_back(std::string("create_new")); + } + if (marketplace_listings_id == mUUID) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("Rename")); + disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("Delete")); + } + + if (isPanelActive("Favorite Items")) + { + disabled_items.push_back(std::string("Delete")); + } + if(trash_id == mUUID) + { + bool is_recent_panel = isPanelActive("Recent Items"); + + // This is the trash. + items.push_back(std::string("Empty Trash")); + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array); + LLViewerInventoryCategory *trash = getCategory(); + // Enable Empty menu item only when there is something to act upon. + // Also don't enable menu if folder isn't fully fetched + if ((0 == cat_array->size() && 0 == item_array->size()) + || is_recent_panel + || !trash + || trash->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN + || trash->getDescendentCount() == LLViewerInventoryCategory::VERSION_UNKNOWN + || gAgentAvatarp->hasAttachmentsInTrash()) + { + disabled_items.push_back(std::string("Empty Trash")); + } + + items.push_back(std::string("thumbnail")); + } + else if(isItemInTrash()) + { + // This is a folder in the trash. + items.clear(); // clear any items that used to exist + addTrashContextMenuOptions(items, disabled_items); + } + else if(isAgentInventory()) // do not allow creating in library + { + LLViewerInventoryCategory *cat = getCategory(); + // BAP removed protected check to re-enable standard ops in untyped folders. + // Not sure what the right thing is to do here. + if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT)) + { + if (!isInboxFolder() // don't allow creation in inbox + && outfits_id != mUUID) + { + bool menu_items_added = false; + // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. + if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) + { + items.push_back(std::string("New Folder")); + menu_items_added = true; + } + if (!isMarketplaceListingsFolder()) + { + items.push_back(std::string("upload_def")); + items.push_back(std::string("create_new")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Material")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); + items.push_back(std::string("New Settings")); + if (!LLEnvironment::instance().isInventoryEnabled()) + { + disabled_items.push_back("New Settings"); + } + } + else + { + items.push_back(std::string("New Listing Folder")); + } + if (menu_items_added) + { + items.push_back(std::string("Create Separator")); + } + } + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + // Want some but not all of the items from getClipboardEntries for outfits. + if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) + { + items.push_back(std::string("Rename")); + items.push_back(std::string("thumbnail")); + + addDeleteContextMenuOptions(items, disabled_items); + // EXT-4030: disallow deletion of currently worn outfit + const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) + { + disabled_items.push_back(std::string("Delete")); + } + } + } + + if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID) + { + items.push_back(std::string("Copy outfit list to clipboard")); + addOpenFolderMenuOptions(flags, items); + } + + //Added by aura to force inventory pull on right-click to display folder options correctly. 07-17-06 + mCallingCards = mWearables = false; + + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (checkFolderForContentsOfType(model, is_callingcard)) + { + mCallingCards=true; + } + + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) + { + mWearables=true; + } + } + else + { + // Mark wearables and allow copy from library + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + const LLInventoryCategory* category = model->getCategory(mUUID); + if (!category) return; + LLFolderType::EType type = category->getPreferredType(); + const bool is_system_folder = LLFolderType::lookupIsProtectedType(type); + + LLFindWearables is_wearable; + LLIsType is_object(LLAssetType::AT_OBJECT); + LLIsType is_gesture(LLAssetType::AT_GESTURE); + + if (checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture)) + { + mWearables = true; + } + + if (!is_system_folder) + { + items.push_back(std::string("Copy")); + if (!isItemCopyable()) + { + // For some reason there are items in library that can't be copied directly + disabled_items.push_back(std::string("Copy")); + } + } + } + + // Preemptively disable system folder removal if more than one item selected. + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Delete System Folder")); + } + + if (isAgentInventory() && !isMarketplaceListingsFolder()) + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + } + + + + // Add menu items that are dependent on the contents of the folder. + LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); + if (category && (marketplace_listings_id != mUUID)) + { + uuid_vec_t folders; + folders.push_back(category->getUUID()); + + sSelf = getHandle(); + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); + fetch->startFetch(); + if (fetch->isFinished()) + { + // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down + // This saves lots of time as buildContextMenu() is called a lot + delete fetch; + buildContextMenuFolderOptions(flags, items, disabled_items); + } + else + { + // it's all on its way - add an observer, and the inventory will call done for us when everything is here. + gInventory.addObserver(fetch); + } + } +} + +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) +{ + // Build folder specific options back up + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + + const LLInventoryCategory* category = model->getCategory(mUUID); + if(!category) return; + + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + if ((trash_id == mUUID) || isItemInTrash()) + { + addOpenFolderMenuOptions(flags, items); + return; + } + + if (!canMenuDelete()) + { + disabled_items.push_back(std::string("Delete")); + } + if (isMarketplaceListingsFolder()) return; + + LLFolderType::EType type = category->getPreferredType(); + const bool is_system_folder = LLFolderType::lookupIsProtectedType(type); + // BAP change once we're no longer treating regular categories as ensembles. + const bool is_agent_inventory = isAgentInventory(); + + // Only enable calling-card related options for non-system folders. + if (!is_system_folder && is_agent_inventory && (mRoot != NULL)) + { + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) + { + items.push_back(std::string("Calling Card Separator")); + items.push_back(std::string("Conference Chat Folder")); + items.push_back(std::string("IM All Contacts In Folder")); + } + + if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren() && (type != LLFolderType::FT_OUTFIT)) + { + items.push_back(std::string("Ungroup folder items")); + } + } + else + { + disabled_items.push_back(std::string("New folder from selected")); + } + + //skip the rest options in single-folder mode + if (mRoot == NULL) + { + return; + } + + addOpenFolderMenuOptions(flags, items); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory) + { + items.push_back(std::string("Delete System Folder")); + } +#endif + + // wearables related functionality for folders. + //is_wearable + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (mWearables || + checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) + { + // Only enable add/replace outfit for non-system folders. + if (!is_system_folder) + { + // Adding an outfit onto another (versus replacing) doesn't make sense. + if (type != LLFolderType::FT_OUTFIT) + { + items.push_back(std::string("Add To Outfit")); + if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID)) + { + disabled_items.push_back(std::string("Add To Outfit")); + } + } + + items.push_back(std::string("Replace Outfit")); + if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) + { + disabled_items.push_back(std::string("Replace Outfit")); + } + } + if (is_agent_inventory) + { + items.push_back(std::string("Folder Wearables Separator")); + // Note: If user tries to unwear "My Inventory", it's going to deactivate everything including gestures + // Might be safer to disable this for "My Inventory" + items.push_back(std::string("Remove From Outfit")); + if (type != LLFolderType::FT_ROOT_INVENTORY // Unless COF is empty, whih shouldn't be, warrantied to have worn items + && !LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) // expensive from root! + { + disabled_items.push_back(std::string("Remove From Outfit")); + } + } + items.push_back(std::string("Outfit Separator")); + + } +} + +// Flags unused +void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + sSelf.markDead(); + + // fetch contents of this folder, as context menu can depend on contents + // still, user would have to open context menu again to see the changes + gInventory.fetchDescendentsOf(getUUID()); + + + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + LL_DEBUGS() << "LLFolderBridge::buildContextMenu()" << LL_ENDL; + + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + + buildContextMenuOptions(flags, items, disabled_items); + hide_context_entries(menu, items, disabled_items); + + // Reposition the menu, in case we're adding items to an existing menu. + menu.needsArrange(); + menu.arrangeAndClear(); +} + +void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items) +{ + if ((flags & ITEM_IN_MULTI_SELECTION) == 0) + { + items.push_back(std::string("open_in_new_window")); + items.push_back(std::string("Open Folder Separator")); + items.push_back(std::string("Copy Separator")); + if(isPanelActive("comb_single_folder_inv")) + { + items.push_back(std::string("open_in_current_window")); + } + } +} + +bool LLFolderBridge::hasChildren() const +{ + LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + LLInventoryModel::EHasChildren has_children; + has_children = gInventory.categoryHasChildren(mUUID); + return has_children != LLInventoryModel::CHILDREN_NO; +} + +bool LLFolderBridge::dragOrDrop(MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) +{ + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + + static LLPointer<LLInventoryCallback> drop_cb = NULL; + LLInventoryPanel* panel = mInventoryPanel.get(); + LLToolDragAndDrop* drop_tool = LLToolDragAndDrop::getInstance(); + if (drop + && panel->getRootFolder()->isSingleFolderMode() + && panel->getRootFolderID() == mUUID + && drop_tool->getCargoIndex() == 0) + { + drop_cb = new LLPasteIntoFolderCallback(mInventoryPanel); + } + + + //LL_INFOS() << "LLFolderBridge::dragOrDrop()" << LL_ENDL; + bool accept = false; + switch(cargo_type) + { + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_CALLINGCARD: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + case DAD_MESH: + case DAD_SETTINGS: + case DAD_MATERIAL: + accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, true, drop_cb); + break; + case DAD_LINK: + // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. + // If we have an item of AT_LINK_FOLDER type we should process the linked + // category being dragged or dropped into folder. + if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType()) + { + LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); + if (linked_category) + { + accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, true, true, drop_cb); + } + } + else + { + accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, true, drop_cb); + } + break; + case DAD_CATEGORY: + if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID)) + { + accept = false; + } + else + { + accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg, false, true, drop_cb); + } + break; + case DAD_ROOT_CATEGORY: + case DAD_NONE: + break; + default: + LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL; + break; + } + + if (!drop || drop_tool->getCargoIndex() + 1 == drop_tool->getCargoCount()) + { + drop_cb = NULL; + } + return accept; +} + +LLViewerInventoryCategory* LLFolderBridge::getCategory() const +{ + LLViewerInventoryCategory* cat = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + cat = (LLViewerInventoryCategory*)model->getCategory(mUUID); + } + return cat; +} + + +// static +void LLFolderBridge::pasteClipboard(void* user_data) +{ + LLFolderBridge* self = (LLFolderBridge*)user_data; + if(self) self->pasteFromClipboard(); +} + +void LLFolderBridge::createNewShirt(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHIRT); +} + +void LLFolderBridge::createNewPants(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_PANTS); +} + +void LLFolderBridge::createNewShoes(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHOES); +} + +void LLFolderBridge::createNewSocks(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SOCKS); +} + +void LLFolderBridge::createNewJacket(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_JACKET); +} + +void LLFolderBridge::createNewSkirt(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SKIRT); +} + +void LLFolderBridge::createNewGloves(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_GLOVES); +} + +void LLFolderBridge::createNewUndershirt(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_UNDERSHIRT); +} + +void LLFolderBridge::createNewUnderpants(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_UNDERPANTS); +} + +void LLFolderBridge::createNewShape(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHAPE); +} + +void LLFolderBridge::createNewSkin(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SKIN); +} + +void LLFolderBridge::createNewHair(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_HAIR); +} + +void LLFolderBridge::createNewEyes(void* user_data) +{ + LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_EYES); +} + +EInventorySortGroup LLFolderBridge::getSortGroup() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + if (preferred_type == LLFolderType::FT_TRASH) + { + return SG_TRASH_FOLDER; + } + + if(LLFolderType::lookupIsProtectedType(preferred_type)) + { + return SG_SYSTEM_FOLDER; + } + + return SG_NORMAL_FOLDER; +} + + +// static +void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::EType type) +{ + if(!bridge) return; + LLUUID parent_id = bridge->getUUID(); + LLAgentWearables::createWearable(type, false, parent_id); +} + +void LLFolderBridge::modifyOutfit(bool append) +{ + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + LLViewerInventoryCategory* cat = getCategory(); + if(!cat) return; + + // checking amount of items to wear + U32 max_items = gSavedSettings.getU32("WearFolderLimit"); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(cat->getUUID(), + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + not_worn); + + if (items.size() > max_items) + { + LLSD args; + args["AMOUNT"] = llformat("%d", max_items); + LLNotificationsUtil::add("TooManyWearables", args); + return; + } + + if (isAgentInventory()) + { + LLAppearanceMgr::instance().wearInventoryCategory(cat, false, append); + } + else + { + // Library, we need to copy content first + LLAppearanceMgr::instance().wearInventoryCategory(cat, true, append); + } +} + +//static +void LLFolderBridge::onCanDeleteIdle(void* user_data) +{ + LLFolderBridge* self = (LLFolderBridge*)user_data; + + // we really need proper onidle mechanics that returns available time + const F32 EXPIRY_SECONDS = 0.008f; + LLTimer timer; + timer.setTimerExpirySec(EXPIRY_SECONDS); + + LLInventoryModel* model = self->getInventoryModel(); + if (model) + { + switch (self->mCanDeleteFolderState) + { + case CDS_INIT_FOLDER_CHECK: + // Can still be expensive, split it further? + model->collectDescendents( + self->mUUID, + self->mFoldersToCheck, + self->mItemsToCheck, + LLInventoryModel::EXCLUDE_TRASH); + self->mCanDeleteFolderState = CDS_PROCESSING_ITEMS; + break; + + case CDS_PROCESSING_ITEMS: + while (!timer.hasExpired() && !self->mItemsToCheck.empty()) + { + LLViewerInventoryItem* item = self->mItemsToCheck.back().get(); + if (item) + { + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item)) + { + if (get_is_item_worn(item)) + { + // At the moment we disable 'cut' if category has worn items (do we need to?) + // but allow 'delete' to happen since it will prompt user to detach + self->mCanCut = false; + } + } + + if (!item->getIsLinkType() && get_is_item_worn(item)) + { + self->mCanCut = false; + } + } + self->mItemsToCheck.pop_back(); + } + self->mCanDeleteFolderState = CDS_PROCESSING_FOLDERS; + break; + case CDS_PROCESSING_FOLDERS: + { + const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + LLViewerInventoryCategory* outfit_linked_category = base_outfit_link ? base_outfit_link->getLinkedCategory() : nullptr; + + while (!timer.hasExpired() && !self->mFoldersToCheck.empty()) + { + LLViewerInventoryCategory* cat = self->mFoldersToCheck.back().get(); + if (cat) + { + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (LLFolderType::lookupIsProtectedType(folder_type)) + { + self->mCanCut = false; + self->mCanDelete = false; + self->completeDeleteProcessing(); + break; + } + + // Can't delete the outfit that is currently being worn. + if (folder_type == LLFolderType::FT_OUTFIT) + { + if (cat == outfit_linked_category) + { + self->mCanCut = false; + self->mCanDelete = false; + self->completeDeleteProcessing(); + break; + } + } + } + self->mFoldersToCheck.pop_back(); + } + } + self->mCanDeleteFolderState = CDS_DONE; + break; + case CDS_DONE: + self->completeDeleteProcessing(); + break; + } + } +} + +bool LLFolderBridge::canMenuDelete() +{ + LLInventoryModel* model = getInventoryModel(); + if (!model) return false; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + if (!category) + { + return false; + } + + S32 version = category->getVersion(); + if (mLastCheckedVersion == version) + { + return mCanDelete; + } + + initCanDeleteProcessing(model, version); + return false; +} + +bool LLFolderBridge::canMenuCut() +{ + LLInventoryModel* model = getInventoryModel(); + if (!model) return false; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + if (!category) + { + return false; + } + + S32 version = category->getVersion(); + if (mLastCheckedVersion == version) + { + return mCanCut; + } + + initCanDeleteProcessing(model, version); + return false; +} + +void LLFolderBridge::initCanDeleteProcessing(LLInventoryModel* model, S32 version) +{ + if (mCanDeleteFolderState == CDS_DONE + || mInProgressVersion != version) + { + if (get_is_category_removable(model, mUUID)) + { + // init recursive check of content + mInProgressVersion = version; + mCanCut = true; + mCanDelete = true; + mCanDeleteFolderState = CDS_INIT_FOLDER_CHECK; + mFoldersToCheck.clear(); + mItemsToCheck.clear(); + gIdleCallbacks.addFunction(onCanDeleteIdle, this); + } + else + { + // no check needed + mCanDelete = false; + mCanCut = false; + mLastCheckedVersion = version; + mCanDeleteFolderState = CDS_DONE; + mFoldersToCheck.clear(); + mItemsToCheck.clear(); + } + } +} + +void LLFolderBridge::completeDeleteProcessing() +{ + LLInventoryModel* model = getInventoryModel(); + LLViewerInventoryCategory* category = model ? (LLViewerInventoryCategory*)model->getCategory(mUUID) : nullptr; + if (model && category && category->getVersion() == mInProgressVersion) + { + mLastCheckedVersion = mInProgressVersion; + mCanDeleteFolderState = CDS_DONE; + gIdleCallbacks.deleteFunction(onCanDeleteIdle, this); + } + else + { + mCanDelete = false; + mCanCut = false; + mLastCheckedVersion = LLViewerInventoryCategory::VERSION_UNKNOWN; + mCanDeleteFolderState = CDS_DONE; + } + + if (mRoot) + { + mRoot->updateMenu(); + } +} + + +// +=================================================+ +// | LLMarketplaceFolderBridge | +// +=================================================+ + +// LLMarketplaceFolderBridge is a specialized LLFolderBridge for use in Marketplace Inventory panels +LLMarketplaceFolderBridge::LLMarketplaceFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : +LLFolderBridge(inventory, root, uuid) +{ + m_depth = depth_nesting_in_marketplace(mUUID); + m_stockCountCache = COMPUTE_STOCK_NOT_EVALUATED; +} + +LLUIImagePtr LLMarketplaceFolderBridge::getIcon() const +{ + return getMarketplaceFolderIcon(false); +} + +LLUIImagePtr LLMarketplaceFolderBridge::getIconOpen() const +{ + return getMarketplaceFolderIcon(true); +} + +LLUIImagePtr LLMarketplaceFolderBridge::getMarketplaceFolderIcon(bool is_open) const +{ + LLFolderType::EType preferred_type = getPreferredType(); + if (!LLMarketplaceData::instance().isUpdating(getUUID())) + { + // Skip computation (expensive) if we're waiting for updates. Use the old value in that case. + m_depth = depth_nesting_in_marketplace(mUUID); + } + if ((preferred_type == LLFolderType::FT_NONE) && (m_depth == 2)) + { + // We override the type when in the marketplace listings folder and only for version folder + preferred_type = LLFolderType::FT_MARKETPLACE_VERSION; + } + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open)); +} + +std::string LLMarketplaceFolderBridge::getLabelSuffix() const +{ + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= FOLDER_LOADING_MESSAGE_DELAY) + { + return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); + } + + std::string suffix = ""; + // Listing folder case + if (LLMarketplaceData::instance().isListed(getUUID())) + { + suffix = llformat("%d",LLMarketplaceData::instance().getListingID(getUUID())); + if (suffix.empty()) + { + suffix = LLTrans::getString("MarketplaceNoID"); + } + suffix = " (" + suffix + ")"; + if (LLMarketplaceData::instance().getActivationState(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceLive") + ")"; + } + } + // Version folder case + else if (LLMarketplaceData::instance().isVersionFolder(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceActive") + ")"; + } + // Add stock amount + bool updating = LLMarketplaceData::instance().isUpdating(getUUID()); + if (!updating) + { + // Skip computation (expensive) if we're waiting for update anyway. Use the old value in that case. + m_stockCountCache = compute_stock_count(getUUID()); + } + if (m_stockCountCache == 0) + { + suffix += " (" + LLTrans::getString("MarketplaceNoStock") + ")"; + } + else if (m_stockCountCache != COMPUTE_STOCK_INFINITE) + { + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + suffix += " (" + LLTrans::getString("MarketplaceStock"); + } + else + { + suffix += " (" + LLTrans::getString("MarketplaceMax"); + } + if (m_stockCountCache == COMPUTE_STOCK_NOT_EVALUATED) + { + suffix += "=" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + else + { + suffix += "=" + llformat("%d", m_stockCountCache) + ")"; + } + } + // Add updating suffix + if (updating) + { + suffix += " (" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + return LLInvFVBridge::getLabelSuffix() + suffix; +} + +LLFontGL::StyleFlags LLMarketplaceFolderBridge::getLabelStyle() const +{ + return (LLMarketplaceData::instance().getActivationState(getUUID()) ? LLFontGL::BOLD : LLFontGL::NORMAL); +} + + + + +// helper stuff +bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, std::shared_ptr<LLMoveInv> move_inv) +{ + LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData; + LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if(option == 0 && object) + { + if (cat_and_wear && cat_and_wear->mWear) // && !cat_and_wear->mFolderResponded) + { + LLInventoryObject::object_list_t inventory_objects; + object->getInventoryContents(inventory_objects); + int contents_count = inventory_objects.size(); + LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count, cat_and_wear->mFolderResponded, + cat_and_wear->mReplace); + + gInventory.addObserver(inventoryObserver); + } + + two_uuids_list_t::iterator move_it; + for (move_it = move_inv->mMoveList.begin(); + move_it != move_inv->mMoveList.end(); + ++move_it) + { + object->moveInventory(move_it->first, move_it->second); + } + + // update the UI. + dialog_refresh_all(); + } + + if (move_inv->mCallback) + { + move_inv->mCallback(option, move_inv->mUserData, move_inv.get()); + } + + move_inv.reset(); //since notification will persist + return false; +} + +void drop_to_favorites_cb(const LLUUID& id, LLPointer<LLInventoryCallback> cb1, LLPointer<LLInventoryCallback> cb2) +{ + cb1->fire(id); + cb2->fire(id); +} + +LLFolderBridge::LLFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) + : LLInvFVBridge(inventory, root, uuid) + , mCallingCards(false) + , mWearables(false) + , mIsLoading(false) + , mShowDescendantsCount(false) + , mCanDeleteFolderState(CDS_DONE) + , mLastCheckedVersion(S32_MIN) + , mInProgressVersion(S32_MIN) + , mCanDelete(false) + , mCanCut(false) +{ +} + +LLFolderBridge::~LLFolderBridge() +{ + gIdleCallbacks.deleteFunction(onCanDeleteIdle, this); +} + +void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb) +{ + // use callback to rearrange favorite landmarks after adding + // to have new one placed before target (on which it was dropped). See EXT-4312. + LLPointer<AddFavoriteLandmarkCallback> cb_fav = new AddFavoriteLandmarkCallback(); + LLInventoryPanel* panel = mInventoryPanel.get(); + LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; + LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL; + if (view_model) + { + cb_fav.get()->setTargetLandmarkId(view_model->getUUID()); + } + + LLPointer <LLInventoryCallback> callback = cb_fav; + if (cb) + { + callback = new LLBoostFuncInventoryCallback(boost::bind(drop_to_favorites_cb, _1, cb, cb_fav)); + } + + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + mUUID, + std::string(), + callback); +} + +void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb) +{ + if((inv_item->getInventoryType() == LLInventoryType::IT_TEXTURE) || (inv_item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)) + { + const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + if(mUUID != my_outifts_id) + { + // Legacy: prior to thumbnails images in outfits were used for outfit gallery. + LLNotificationsUtil::add("ThumbnailOutfitPhoto"); + } + return; + } + + // BAP - should skip if dup. + if (move_is_into_current_outfit) + { + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true); + } + else + { + LLPointer<LLInventoryCallback> cb = NULL; + link_inventory_object(mUUID, LLConstPointer<LLInventoryObject>(inv_item), cb); + } +} + +void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb) +{ + // make a folder in the My Outfits directory. + const LLUUID dest_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + // Note: creation will take time, so passing folder id to callback is slightly unreliable, + // but so is collecting and passing descendants' ids + inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb); + gInventory.createNewCategory(dest_id, + LLFolderType::FT_OUTFIT, + inv_cat->getName(), + func, + inv_cat->getThumbnailUUID()); +} + +void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb) +{ + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + getInventoryModel()->getDirectDescendentsOf(cat_source_id, categories, items); + + LLInventoryObject::const_object_list_t link_array; + + + LLInventoryModel::item_array_t::iterator iter = items->begin(); + LLInventoryModel::item_array_t::iterator end = items->end(); + while (iter!=end) + { + const LLViewerInventoryItem* item = (*iter); + // By this point everything is supposed to be filtered, + // but there was a delay to create folder so something could have changed + LLInventoryType::EType inv_type = item->getInventoryType(); + if ((inv_type == LLInventoryType::IT_WEARABLE) || + (inv_type == LLInventoryType::IT_GESTURE) || + (inv_type == LLInventoryType::IT_ATTACHMENT) || + (inv_type == LLInventoryType::IT_OBJECT) || + (inv_type == LLInventoryType::IT_SNAPSHOT) || + (inv_type == LLInventoryType::IT_TEXTURE)) + { + link_array.push_back(LLConstPointer<LLInventoryObject>(item)); + } + iter++; + } + + if (!link_array.empty()) + { + link_inventory_array(cat_dest_id, link_array, cb); + } +} + +// Callback for drop item if DAMA required... +void LLFolderBridge::callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + std::string tooltip_msg; + dragItemIntoFolder(inv_item, true, tooltip_msg, false); + } +} + +// Callback for drop category if DAMA required... +void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + std::string tooltip_msg; + dragCategoryIntoFolder(inv_category, true, tooltip_msg, false, false); + } +} + +// This is used both for testing whether an item can be dropped +// into the folder, as well as performing the actual drop, depending +// if drop == true. +bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, + bool drop, + std::string& tooltip_msg, + bool user_confirm, + LLPointer<LLInventoryCallback> cb) +{ + LLInventoryModel* model = getInventoryModel(); + + if (!model || !inv_item) return false; + if (!isAgentInventory()) return false; // cannot drag into library + if (!isAgentAvatarValid()) return false; + + LLInventoryPanel* destination_panel = mInventoryPanel.get(); + if (!destination_panel) return false; + + LLInventoryFilter* filter = getInventoryFilter(); + if (!filter) return false; + + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID from_folder_uuid = inv_item->getParentUUID(); + + const bool move_is_into_current_outfit = (mUUID == current_outfit_id); + const bool move_is_into_favorites = (mUUID == favorites_id); + const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const bool move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); + const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const bool move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id); + + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + bool accept = false; + U64 filter_types = filter->getFilterTypes(); + // We shouldn't allow to drop non recent items into recent tab (or some similar transactions) + // while we are allowing to interact with regular filtered inventory + bool use_filter = filter_types && (filter_types&LLInventoryFilter::FILTERTYPE_DATE || (filter_types&LLInventoryFilter::FILTERTYPE_OBJECT)==0); + LLViewerObject* object = NULL; + if(LLToolDragAndDrop::SOURCE_AGENT == source) + { + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + + const bool move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); + const bool move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); + + //-------------------------------------------------------------------------------- + // Determine if item can be moved. + // + + bool is_movable = true; + + switch (inv_item->getActualType()) + { + case LLAssetType::AT_CATEGORY: + is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType()); + break; + default: + break; + } + // Can't explicitly drag things out of the COF. + if (move_is_outof_current_outfit) + { + is_movable = false; + } + if (move_is_into_trash) + { + is_movable &= inv_item->getIsLinkType() || !get_is_item_worn(inv_item->getUUID()); + } + if (is_movable) + { + // Don't allow creating duplicates in the Calling Card/Friends + // subfolders, see bug EXT-1599. Check is item direct descendent + // of target folder and forbid item's movement if it so. + // Note: isItemDirectDescendentOfCategory checks if + // passed category is in the Calling Card/Friends folder + is_movable &= !LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(inv_item, getCategory()); + } + + // + //-------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------------- + // Determine if item can be moved & dropped + // Note: if user_confirm is false, we already went through those accept logic test and can skip them + + accept = true; + + if (user_confirm && !is_movable) + { + accept = false; + } + else if (user_confirm && (mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) + { + accept = false; + } + else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit)) + { + accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); + } + else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks)) + { + accept = can_move_to_landmarks(inv_item); + } + else if (user_confirm && move_is_into_marketplacelistings) + { + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); + LLViewerInventoryCategory * dest_folder = getCategory(); + accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex()); + } + + // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility) + if (user_confirm && accept) + { + LLViewerInventoryCategory * dest_folder = getCategory(); + accept = dest_folder->acceptItem(inv_item); + } + + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(false); + + // Check whether the item being dragged from active inventory panel + // passes the filter of the destination panel. + if (user_confirm && accept && active_panel && use_filter) + { + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); + if (!fv_item) return false; + + accept = filter->check(fv_item->getViewModelItem()); + } + + if (accept && drop) + { + if (inv_item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash) + { + LLGestureMgr::instance().deactivateGesture(inv_item->getUUID()); + } + // If an item is being dragged between windows, unselect everything in the active window + // so that we don't follow the selection to its new location (which is very annoying). + // RN: a better solution would be to deselect automatically when an item is moved + // and then select any item that is dropped only in the panel that it is dropped in + if (active_panel && (destination_panel != active_panel)) + { + active_panel->unSelectAll(); + } + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + if ((move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(inv_item->getUUID()) + || LLMarketplaceData::instance().isListedAndActive(inv_item->getUUID()))) || + (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID))) + { + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); + return true; + } + if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings) + { + LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); + return true; + } + } + + //-------------------------------------------------------------------------------- + // Destination folder logic + // + + // REORDER + // (only reorder the item in Favorites folder) + if ((mUUID == inv_item->getParentUUID()) && move_is_into_favorites) + { + LLFolderViewItem* itemp = destination_panel->getRootFolder()->getDraggingOverItem(); + if (itemp) + { + LLUUID srcItemId = inv_item->getUUID(); + LLUUID destItemId = static_cast<LLFolderViewModelItemInventory*>(itemp->getViewModelItem())->getUUID(); + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId); + } + } + + // FAVORITES folder + // (copy the item) + else if (move_is_into_favorites) + { + dropToFavorites(inv_item, cb); + } + // CURRENT OUTFIT or OUTFIT folder + // (link the item) + else if (move_is_into_current_outfit || move_is_into_outfit) + { + dropToOutfit(inv_item, move_is_into_current_outfit, cb); + } + // MARKETPLACE LISTINGS folder + // Move the item + else if (move_is_into_marketplacelistings) + { + move_item_to_marketplacelistings(inv_item, mUUID); + if (cb) cb->fire(inv_item->getUUID()); + } + // NORMAL or TRASH folder + // (move the item, restamp if into trash) + else + { + // set up observer to select item once drag and drop from inbox is complete + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX))) + { + set_dad_inbox_object(inv_item->getUUID()); + } + + LLInvFVBridge::changeItemParent( + model, + (LLViewerInventoryItem*)inv_item, + mUUID, + move_is_into_trash); + if (cb) cb->fire(inv_item->getUUID()); + } + + if (move_is_from_marketplacelistings) + { + // If we move from an active (listed) listing, checks that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) + { + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + }); + } + } + + // + //-------------------------------------------------------------------------------- + } + } + else if (LLToolDragAndDrop::SOURCE_WORLD == source) + { + // Make sure the object exists. If we allowed dragging from + // anonymous objects, it would be possible to bypass + // permissions. + object = gObjectList.findObject(inv_item->getParentUUID()); + if (!object) + { + LL_INFOS() << "Object not found for drop." << LL_ENDL; + return false; + } + + // coming from a task. Need to figure out if the person can + // move/copy this item. + LLPermissions perm(inv_item->getPermissions()); + bool is_move = false; + if ((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) + && perm.allowTransferTo(gAgent.getID()))) + // || gAgent.isGodlike()) + { + accept = true; + } + else if(object->permYouOwner()) + { + // If the object cannot be copied, but the object the + // inventory is owned by the agent, then the item can be + // moved from the task to agent inventory. + is_move = true; + accept = true; + } + + // Don't allow placing an original item into Current Outfit or an outfit folder + // because they must contain only links to wearable items. + // *TODO: Probably we should create a link to an item if it was dragged to outfit or COF. + if (move_is_into_current_outfit || move_is_into_outfit) + { + accept = false; + } + // Don't allow to move a single item to Favorites or Landmarks + // if it is not a landmark or a link to a landmark. + else if ((move_is_into_favorites || move_is_into_landmarks) + && !can_move_to_landmarks(inv_item)) + { + accept = false; + } + else if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = false; + } + + // Check whether the item being dragged from in world + // passes the filter of the destination panel. + if (accept && use_filter) + { + accept = filter->check(inv_item); + } + + if (accept && drop) + { + LLUUID item_id = inv_item->getUUID(); + std::shared_ptr<LLMoveInv> move_inv (new LLMoveInv()); + move_inv->mObjectID = inv_item->getParentUUID(); + two_uuids_t item_pair(mUUID, item_id); + move_inv->mMoveList.push_back(item_pair); + if (cb) + { + move_inv->mCallback = [item_id, cb](S32, void*, const LLMoveInv* move_inv) mutable + { cb->fire(item_id); }; + } + move_inv->mUserData = NULL; + if(is_move) + { + warn_move_inventory(object, move_inv); + } + else + { + // store dad inventory item to select added one later. See EXT-4347 + set_dad_inventory_item(inv_item, mUUID); + + LLNotification::Params params("MoveInventoryFromObject"); + params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); + LLNotifications::instance().forceResponse(params, 0); + } + } + } + else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) + { + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = false; + } + else if ((inv_item->getActualType() == LLAssetType::AT_SETTINGS) && !LLEnvironment::instance().isInventoryEnabled()) + { + tooltip_msg = LLTrans::getString("NoEnvironmentSettings"); + accept = false; + } + else + { + // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder + // because they must contain only links to wearable items. + accept = !(move_is_into_current_outfit || move_is_into_outfit); + } + + // Check whether the item being dragged from notecard + // passes the filter of the destination panel. + if (accept && use_filter) + { + accept = filter->check(inv_item); + } + + if (accept && drop) + { + copy_inventory_from_notecard(mUUID, // Drop to the chosen destination folder + LLToolDragAndDrop::getInstance()->getObjectID(), + LLToolDragAndDrop::getInstance()->getSourceID(), + inv_item); + } + } + else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item; + if(item && item->isFinished()) + { + accept = true; + + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = false; + } + else if (move_is_into_current_outfit || move_is_into_outfit) + { + accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); + } + // Don't allow to move a single item to Favorites or Landmarks + // if it is not a landmark or a link to a landmark. + else if (move_is_into_favorites || move_is_into_landmarks) + { + accept = can_move_to_landmarks(inv_item); + } + + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(false); + + // Check whether the item being dragged from the library + // passes the filter of the destination panel. + if (accept && active_panel && use_filter) + { + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); + if (!fv_item) return false; + + accept = filter->check(fv_item->getViewModelItem()); + } + + if (accept && drop) + { + // FAVORITES folder + // (copy the item) + if (move_is_into_favorites) + { + dropToFavorites(inv_item, cb); + } + // CURRENT OUTFIT or OUTFIT folder + // (link the item) + else if (move_is_into_current_outfit || move_is_into_outfit) + { + dropToOutfit(inv_item, move_is_into_current_outfit, cb); + } + else + { + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + mUUID, + std::string(), + cb); + } + } + } + } + else + { + LL_WARNS() << "unhandled drag source" << LL_ENDL; + } + return accept; +} + +// static +bool check_category(LLInventoryModel* model, + const LLUUID& cat_id, + LLInventoryPanel* active_panel, + LLInventoryFilter* filter) +{ + if (!model || !active_panel || !filter) + return false; + + if (!filter->checkFolder(cat_id)) + { + return false; + } + + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + model->collectDescendents(cat_id, descendent_categories, descendent_items, true); + + S32 num_descendent_categories = descendent_categories.size(); + S32 num_descendent_items = descendent_items.size(); + + if (num_descendent_categories + num_descendent_items == 0) + { + // Empty folder should be checked as any other folder view item. + // If we are filtering by date the folder should not pass because + // it doesn't have its own creation date. See LLInvFVBridge::getCreationDate(). + return check_item(cat_id, active_panel, filter); + } + + for (S32 i = 0; i < num_descendent_categories; ++i) + { + LLInventoryCategory* category = descendent_categories[i]; + if(!check_category(model, category->getUUID(), active_panel, filter)) + { + return false; + } + } + + for (S32 i = 0; i < num_descendent_items; ++i) + { + LLViewerInventoryItem* item = descendent_items[i]; + if(!check_item(item->getUUID(), active_panel, filter)) + { + return false; + } + } + + return true; +} + +// static +bool check_item(const LLUUID& item_id, + LLInventoryPanel* active_panel, + LLInventoryFilter* filter) +{ + if (!active_panel || !filter) return false; + + LLFolderViewItem* fv_item = active_panel->getItemByID(item_id); + if (!fv_item) return false; + + return filter->check(fv_item->getViewModelItem()); +} + +// +=================================================+ +// | LLTextureBridge | +// +=================================================+ + +LLUIImagePtr LLTextureBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_TEXTURE, mInvType); +} + +void LLTextureBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +} + +bool LLTextureBridge::canSaveTexture(void) +{ + const LLInventoryModel* model = getInventoryModel(); + if(!model) + { + return false; + } + + const LLViewerInventoryItem *item = model->getItem(mUUID); + if (item) + { + return item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); + } + return false; +} + +void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLTextureBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + + addOpenRightClickMenuOption(items); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + + items.push_back(std::string("Texture Separator")); + + if ((flags & ITEM_IN_MULTI_SELECTION) != 0) + { + items.push_back(std::string("Save Selected As")); + } + else + { + items.push_back(std::string("Save As")); + if (!canSaveTexture()) + { + disabled_items.push_back(std::string("Save As")); + } + } + + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +// virtual +void LLTextureBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("save_as" == action) + { + LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUID); + if (preview_texture) + { + preview_texture->openToSave(); + preview_texture->saveAs(); + } + } + else if ("save_selected_as" == action) + { + openItem(); + if (canSaveTexture()) + { + LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUID); + if (preview_texture) + { + preview_texture->saveMultipleToFile(mFileName); + } + } + else + { + LL_WARNS() << "You don't have permission to save " << getName() << " to disk." << LL_ENDL; + } + + } + else LLItemBridge::performAction(model, action); +} + +// +=================================================+ +// | LLSoundBridge | +// +=================================================+ + +void LLSoundBridge::openItem() +{ + const LLViewerInventoryItem* item = getItem(); + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +} + +void LLSoundBridge::openSoundPreview(void* which) +{ + LLSoundBridge *me = (LLSoundBridge *)which; + LLFloaterReg::showInstance("preview_sound", LLSD(me->mUUID), TAKE_FOCUS_YES); +} + +void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLSoundBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + if (isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + items.push_back(std::string("Sound Open")); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + } + + items.push_back(std::string("Sound Separator")); + items.push_back(std::string("Sound Play")); + } + + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +void LLSoundBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("sound_play" == action) + { + LLViewerInventoryItem* item = getItem(); + if(item) + { + send_sound_trigger(item->getAssetUUID(), SOUND_GAIN); + } + } + else if ("open" == action) + { + openSoundPreview((void*)this); + } + else LLItemBridge::performAction(model, action); +} + +// +=================================================+ +// | LLLandmarkBridge | +// +=================================================+ + +LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags/* = 0x00*/) : + LLItemBridge(inventory, root, uuid) +{ + mVisited = false; + if (flags & LLInventoryItemFlags::II_FLAGS_LANDMARK_VISITED) + { + mVisited = true; + } +} + +LLUIImagePtr LLLandmarkBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited, false); +} + +void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + LL_DEBUGS() << "LLLandmarkBridge::buildContextMenu()" << LL_ENDL; + if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + items.push_back(std::string("Landmark Open")); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + } + + items.push_back(std::string("Landmark Separator")); + items.push_back(std::string("url_copy")); + items.push_back(std::string("About Landmark")); + items.push_back(std::string("show_on_map")); + } + + // Disable "About Landmark" menu item for + // multiple landmarks selected. Only one landmark + // info panel can be shown at a time. + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("url_copy")); + disabled_items.push_back(std::string("About Landmark")); + } + + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +// Convenience function for the two functions below. +void teleport_via_landmark(const LLUUID& asset_id) +{ + gAgent.teleportViaLandmark( asset_id ); + + // we now automatically track the landmark you're teleporting to + // because you'll probably arrive at a telehub instead + LLFloaterWorldMap* floater_world_map = LLFloaterWorldMap::getInstance(); + if( floater_world_map ) + { + floater_world_map->trackLandmark( asset_id ); + } +} + +// virtual +void LLLandmarkBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("teleport" == action) + { + LLViewerInventoryItem* item = getItem(); + if(item) + { + teleport_via_landmark(item->getAssetUUID()); + } + } + else if ("about" == action) + { + LLViewerInventoryItem* item = getItem(); + if(item) + { + LLSD key; + key["type"] = "landmark"; + key["id"] = item->getUUID(); + + LLFloaterSidePanelContainer::showPanel("places", key); + } + } + else + { + LLItemBridge::performAction(model, action); + } +} + +static bool open_landmark_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + LLUUID asset_id = notification["payload"]["asset_id"].asUUID(); + if (option == 0) + { + teleport_via_landmark(asset_id); + } + + return false; +} +static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFromLandmark", open_landmark_callback); + + +void LLLandmarkBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +} + + +// +=================================================+ +// | LLCallingCardObserver | +// +=================================================+ +class LLCallingCardObserver : public LLFriendObserver +{ +public: + LLCallingCardObserver(LLCallingCardBridge* bridge) : mBridgep(bridge) {} + virtual ~LLCallingCardObserver() { mBridgep = NULL; } + virtual void changed(U32 mask) + { + if (mask & LLFriendObserver::ONLINE) + { + mBridgep->refreshFolderViewItem(); + mBridgep->checkSearchBySuffixChanges(); + } + } +protected: + LLCallingCardBridge* mBridgep; +}; + +// +=================================================+ +// | LLCallingCardBridge | +// +=================================================+ + +LLCallingCardBridge::LLCallingCardBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid ) : + LLItemBridge(inventory, root, uuid) +{ + mObserver = new LLCallingCardObserver(this); + mCreatorUUID = getItem()->getCreatorUUID(); + LLAvatarTracker::instance().addParticularFriendObserver(mCreatorUUID, mObserver); +} + +LLCallingCardBridge::~LLCallingCardBridge() +{ + LLAvatarTracker::instance().removeParticularFriendObserver(mCreatorUUID, mObserver); + + delete mObserver; +} + +void LLCallingCardBridge::refreshFolderViewItem() +{ + LLInventoryPanel* panel = mInventoryPanel.get(); + LLFolderViewItem* itemp = panel ? panel->getItemByID(mUUID) : NULL; + if (itemp) + { + itemp->refresh(); + } +} + +void LLCallingCardBridge::checkSearchBySuffixChanges() +{ + if (!mDisplayName.empty()) + { + // changes in mDisplayName are processed by rename function and here it will be always same + // suffixes are also of fixed length, and we are processing change of one at a time, + // so it should be safe to use length (note: mSearchableName is capitalized) + S32 old_length = mSearchableName.length(); + S32 new_length = mDisplayName.length() + getLabelSuffix().length(); + if (old_length == new_length) + { + return; + } + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); + if (new_length<old_length) + { + LLInventoryFilter* filter = getInventoryFilter(); + if (filter && mPassedFilter && mSearchableName.find(filter->getFilterSubString()) == std::string::npos) + { + // string no longer contains substring + // we either have to update all parents manually or restart filter. + // dirtyFilter will not work here due to obsolete descendants' generations + getInventoryFilter()->setModified(LLFolderViewFilter::FILTER_MORE_RESTRICTIVE); + } + } + else + { + if (getInventoryFilter()) + { + // mSearchableName became longer, we gained additional suffix and need to repeat filter check. + dirtyFilter(); + } + } + } +} + +// virtual +void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("begin_im" == action) + { + LLViewerInventoryItem *item = getItem(); + if (item && (item->getCreatorUUID() != gAgent.getID()) && + (!item->getCreatorUUID().isNull())) + { + std::string callingcard_name = LLCacheName::getDefaultName(); + LLAvatarName av_name; + if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) + { + callingcard_name = av_name.getCompleteName(); + } + LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); + if (session_id != LLUUID::null) + { + LLFloaterIMContainer::getInstance()->showConversation(session_id); + } + } + } + else if ("lure" == action) + { + LLViewerInventoryItem *item = getItem(); + if (item && (item->getCreatorUUID() != gAgent.getID()) && + (!item->getCreatorUUID().isNull())) + { + LLAvatarActions::offerTeleport(item->getCreatorUUID()); + } + } + else if ("request_lure" == action) + { + LLViewerInventoryItem *item = getItem(); + if (item && (item->getCreatorUUID() != gAgent.getID()) && + (!item->getCreatorUUID().isNull())) + { + LLAvatarActions::teleportRequest(item->getCreatorUUID()); + } + } + + else LLItemBridge::performAction(model, action); +} + +LLUIImagePtr LLCallingCardBridge::getIcon() const +{ + bool online = false; + LLViewerInventoryItem* item = getItem(); + if(item) + { + online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); + } + return LLInventoryIcon::getIcon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online, false); +} + +std::string LLCallingCardBridge::getLabelSuffix() const +{ + LLViewerInventoryItem* item = getItem(); + if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) ) + { + return LLItemBridge::getLabelSuffix() + " online"; + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +void LLCallingCardBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +/* + LLViewerInventoryItem* item = getItem(); + if(item && !item->getCreatorUUID().isNull()) + { + LLAvatarActions::showProfile(item->getCreatorUUID()); + } +*/ +} + +void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLCallingCardBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Open")); + } + addOpenRightClickMenuOption(items); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + + LLInventoryItem* item = getItem(); + bool good_card = (item + && (LLUUID::null != item->getCreatorUUID()) + && (item->getCreatorUUID() != gAgent.getID())); + bool user_online = false; + if (item) + { + user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID())); + } + items.push_back(std::string("Send Instant Message Separator")); + items.push_back(std::string("Send Instant Message")); + items.push_back(std::string("Offer Teleport...")); + items.push_back(std::string("Request Teleport...")); + items.push_back(std::string("Conference Chat")); + + if (!good_card) + { + disabled_items.push_back(std::string("Send Instant Message")); + } + if (!good_card || !user_online) + { + disabled_items.push_back(std::string("Offer Teleport...")); + disabled_items.push_back(std::string("Request Teleport...")); + disabled_items.push_back(std::string("Conference Chat")); + } + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +bool LLCallingCardBridge::dragOrDrop(MASK mask, bool drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) +{ + LLViewerInventoryItem* item = getItem(); + bool rv = false; + if(item) + { + // check the type + switch(cargo_type) + { + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + case DAD_MESH: + case DAD_SETTINGS: + case DAD_MATERIAL: + { + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + const LLPermissions& perm = inv_item->getPermissions(); + if(gInventory.getItem(inv_item->getUUID()) + && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID())) + { + rv = true; + if(drop) + { + LLGiveInventory::doGiveInventoryItem(item->getCreatorUUID(), + (LLInventoryItem*)cargo_data); + } + } + else + { + // It's not in the user's inventory (it's probably in + // an object's contents), so disallow dragging it here. + // You can't give something you don't yet have. + rv = false; + } + break; + } + case DAD_CATEGORY: + { + LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data; + if( gInventory.getCategory( inv_cat->getUUID() ) ) + { + rv = true; + if(drop) + { + LLGiveInventory::doGiveInventoryCategory( + item->getCreatorUUID(), + inv_cat); + } + } + else + { + // It's not in the user's inventory (it's probably in + // an object's contents), so disallow dragging it here. + // You can't give something you don't yet have. + rv = false; + } + break; + } + default: + break; + } + } + return rv; +} + +// +=================================================+ +// | LLNotecardBridge | +// +=================================================+ + +void LLNotecardBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +} + +void LLNotecardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLNotecardBridge::buildContextMenu()" << LL_ENDL; + + if (isMarketplaceListingsFolder()) + { + menuentry_vec_t items; + menuentry_vec_t disabled_items; + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + hide_context_entries(menu, items, disabled_items); + } + else + { + LLItemBridge::buildContextMenu(menu, flags); + } +} + +// +=================================================+ +// | LLGestureBridge | +// +=================================================+ + +LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const +{ + if( LLGestureMgr::instance().isGestureActive(mUUID) ) + { + return LLFontGL::BOLD; + } + else + { + return LLFontGL::NORMAL; + } +} + +std::string LLGestureBridge::getLabelSuffix() const +{ + if( LLGestureMgr::instance().isGestureActive(mUUID) ) + { + LLStringUtil::format_map_t args; + args["[GESLABEL]"] = LLItemBridge::getLabelSuffix(); + return LLTrans::getString("ActiveGesture", args); + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +// virtual +void LLGestureBridge::performAction(LLInventoryModel* model, std::string action) +{ + if (isAddAction(action)) + { + LLGestureMgr::instance().activateGesture(mUUID); + + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + if (!item) return; + + // Since we just changed the suffix to indicate (active) + // the server doesn't need to know, just the viewer. + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + else if ("deactivate" == action || isRemoveAction(action)) + { + LLGestureMgr::instance().deactivateGesture(mUUID); + + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + if (!item) return; + + // Since we just changed the suffix to indicate (active) + // the server doesn't need to know, just the viewer. + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + else if("play" == action) + { + if(!LLGestureMgr::instance().isGestureActive(mUUID)) + { + // we need to inform server about gesture activating to be consistent with LLPreviewGesture and LLGestureComboList. + bool inform_server = true; + bool deactivate_similar = false; + LLGestureMgr::instance().setGestureLoadedCallback(mUUID, boost::bind(&LLGestureBridge::playGesture, mUUID)); + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + llassert(item); + if (item) + { + LLGestureMgr::instance().activateGestureWithAsset(mUUID, item->getAssetUUID(), inform_server, deactivate_similar); + } + } + else + { + playGesture(mUUID); + } + } + else LLItemBridge::performAction(model, action); +} + +void LLGestureBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +/* + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null); + preview->setFocus(true); + } +*/ +} + +bool LLGestureBridge::removeItem() +{ + // Grab class information locally since *this may be deleted + // within this function. Not a great pattern... + const LLInventoryModel* model = getInventoryModel(); + if(!model) + { + return false; + } + const LLUUID item_id = mUUID; + + // This will also force close the preview window, if it exists. + // This may actually delete *this, if mUUID is in the COF. + LLGestureMgr::instance().deactivateGesture(item_id); + + // If deactivateGesture deleted *this, then return out immediately. + if (!model->getObject(item_id)) + { + return true; + } + + return LLItemBridge::removeItem(); +} + +void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLGestureBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + + addOpenRightClickMenuOption(items); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + + items.push_back(std::string("Gesture Separator")); + if (LLGestureMgr::instance().isGestureActive(getUUID())) + { + items.push_back(std::string("Deactivate")); + } + else + { + items.push_back(std::string("Activate")); + } + items.push_back(std::string("PlayGesture")); + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +// static +void LLGestureBridge::playGesture(const LLUUID& item_id) +{ + if (LLGestureMgr::instance().isGesturePlaying(item_id)) + { + LLGestureMgr::instance().stopGesture(item_id); + } + else + { + LLGestureMgr::instance().playGesture(item_id); + } +} + + +// +=================================================+ +// | LLAnimationBridge | +// +=================================================+ + +void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + LL_DEBUGS() << "LLAnimationBridge::buildContextMenu()" << LL_ENDL; + if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + items.push_back(std::string("Animation Open")); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + } + + items.push_back(std::string("Animation Separator")); + items.push_back(std::string("Animation Play")); + items.push_back(std::string("Animation Audition")); + } + + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +// virtual +void LLAnimationBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ((action == "playworld") || (action == "playlocal")) + { + if (getItem()) + { + LLSD::String activate = "NONE"; + if ("playworld" == action) activate = "Inworld"; + if ("playlocal" == action) activate = "Locally"; + + LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID)); + if (preview) + { + preview->play(activate); + } + } + } + else + { + LLItemBridge::performAction(model, action); + } +} + +void LLAnimationBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +/* + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES); + } +*/ +} + +// +=================================================+ +// | LLObjectBridge | +// +=================================================+ + +// static +LLUUID LLObjectBridge::sContextMenuItemID; + +LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + LLInventoryType::EType type, + U32 flags) : + LLItemBridge(inventory, root, uuid) +{ + mAttachPt = (flags & 0xff); // low bye of inventory flags + mIsMultiObject = is_flag_set(flags, LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS); + mInvType = type; +} + +LLUIImagePtr LLObjectBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject); +} + +LLInventoryObject* LLObjectBridge::getObject() const +{ + LLInventoryObject* object = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + object = (LLInventoryObject*)model->getObject(mUUID); + } + return object; +} + +LLViewerInventoryItem* LLObjectBridge::getItem() const +{ + LLInventoryModel* model = getInventoryModel(); + if (model) + { + return model->getItem(mUUID); + } + return NULL; +} + +LLViewerInventoryCategory* LLObjectBridge::getCategory() const +{ + LLInventoryModel* model = getInventoryModel(); + if (model) + { + return model->getCategory(mUUID); + } + return NULL; +} + +// virtual +void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) +{ + if (isAddAction(action)) + { + LLUUID object_id = mUUID; + LLViewerInventoryItem* item; + item = (LLViewerInventoryItem*)gInventory.getItem(object_id); + if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID())) + { + rez_attachment(item, NULL, true); // Replace if "Wear"ing. + } + else if(item && item->isFinished()) + { + // must be in library. copy it to our inventory and put it on. + LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0)); + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + LLUUID::null, + std::string(), + cb); + } + gFocusMgr.setKeyboardFocus(NULL); + } + else if ("wear_add" == action) + { + LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. + } + else if ("touch" == action) + { + handle_attachment_touch(mUUID); + } + else if ("edit" == action) + { + handle_attachment_edit(mUUID); + } + else if (isRemoveAction(action)) + { + LLAppearanceMgr::instance().removeItemFromAvatar(mUUID); + } + else LLItemBridge::performAction(model, action); +} + +void LLObjectBridge::openItem() +{ + // object double-click action is to wear/unwear object + performAction(getInventoryModel(), + get_is_item_worn(mUUID) ? "detach" : "attach"); +} + +std::string LLObjectBridge::getLabelSuffix() const +{ + if (get_is_item_worn(mUUID)) + { + if (!isAgentAvatarValid()) // Error condition, can't figure out attach point + { + return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn"); + } + std::string attachment_point_name; + if (gAgentAvatarp->getAttachedPointName(mUUID, attachment_point_name)) + { + LLStringUtil::format_map_t args; + args["[ATTACHMENT_POINT]"] = LLTrans::getString(attachment_point_name); + + return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args); + } + else + { + LLStringUtil::format_map_t args; + args["[ATTACHMENT_ERROR]"] = LLTrans::getString(attachment_point_name); + return LLItemBridge::getLabelSuffix() + LLTrans::getString("AttachmentErrorMessage", args); + } + } + return LLItemBridge::getLabelSuffix(); +} + +void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace) +{ + const LLUUID& item_id = item->getLinkedUUID(); + + // Check for duplicate request. + if (isAgentAvatarValid() && + gAgentAvatarp->isWearingAttachment(item_id)) + { + LL_WARNS() << "ATT duplicate attachment request, ignoring" << LL_ENDL; + return; + } + + S32 attach_pt = 0; + if (isAgentAvatarValid() && attachment) + { + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter) + { + if (iter->second == attachment) + { + attach_pt = iter->first; + break; + } + } + } + + LLSD payload; + payload["item_id"] = item_id; // Wear the base object in case this is a link. + payload["attachment_point"] = attach_pt; + payload["is_add"] = !replace; + + if (replace && + (attachment && attachment->getNumObjects() > 0)) + { + LLNotificationsUtil::add("ReplaceAttachment", LLSD(), payload, confirm_attachment_rez); + } + else + { + LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/); + } +} + +bool confirm_attachment_rez(const LLSD& notification, const LLSD& response) +{ + if (!gAgentAvatarp->canAttachMoreObjects()) + { + LLSD args; + args["MAX_ATTACHMENTS"] = llformat("%d", gAgentAvatarp->getMaxAttachments()); + LLNotificationsUtil::add("MaxAttachmentsOnOutfit", args); + return false; + } + + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0/*YES*/) + { + LLUUID item_id = notification["payload"]["item_id"].asUUID(); + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + + if (itemp) + { + // Queue up attachments to be sent in next idle tick, this way the + // attachments are batched up all into one message versus each attachment + // being sent in its own separate attachments message. + U8 attachment_pt = notification["payload"]["attachment_point"].asInteger(); + bool is_add = notification["payload"]["is_add"].asBoolean(); + + LL_DEBUGS("Avatar") << "ATT calling addAttachmentRequest " << (itemp ? itemp->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL; + LLAttachmentsMgr::instance().addAttachmentRequest(item_id, attachment_pt, is_add); + } + } + return false; +} +static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_attachment_rez); + +void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + + LLObjectBridge::sContextMenuItemID = mUUID; + + LLInventoryItem *item = getItem(); + if(item) + { + if (!isAgentAvatarValid()) return; + + if( get_is_item_worn( mUUID ) ) + { + items.push_back(std::string("Wearable And Object Separator")); + + items.push_back(std::string("Attachment Touch")); + if ( ((flags & FIRST_SELECTED_ITEM) == 0) || !enable_attachment_touch(mUUID) ) + { + disabled_items.push_back(std::string("Attachment Touch")); + } + + items.push_back(std::string("Wearable Edit")); + if ( ((flags & FIRST_SELECTED_ITEM) == 0) || !get_is_item_editable(mUUID) ) + { + disabled_items.push_back(std::string("Wearable Edit")); + } + + items.push_back(std::string("Detach From Yourself")); + } + else if (!isItemInTrash() && !isLinkedObjectInTrash() && !isLinkedObjectMissing() && !isCOFFolder()) + { + items.push_back(std::string("Wearable And Object Separator")); + items.push_back(std::string("Wearable And Object Wear")); + items.push_back(std::string("Wearable Add")); + items.push_back(std::string("Attach To")); + items.push_back(std::string("Attach To HUD")); + // commented out for DEV-32347 + //items.push_back(std::string("Restore to Last Position")); + + if (!gAgentAvatarp->canAttachMoreObjects()) + { + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); + disabled_items.push_back(std::string("Attach To")); + disabled_items.push_back(std::string("Attach To HUD")); + } + LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", true); + LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", true); + if (attach_menu + && (attach_menu->getChildCount() == 0) + && attach_hud_menu + && (attach_hud_menu->getChildCount() == 0) + && isAgentAvatarValid()) + { + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ) + { + LLVOAvatar::attachment_map_t::iterator curiter = iter++; + LLViewerJointAttachment* attachment = curiter->second; + LLMenuItemCallGL::Params p; + std::string submenu_name = attachment->getName(); + if (LLTrans::getString(submenu_name) != "") + { + p.name = (" ")+LLTrans::getString(submenu_name)+" "; + } + else + { + p.name = submenu_name; + } + LLSD cbparams; + cbparams["index"] = curiter->first; + cbparams["label"] = p.name; + p.on_click.function_name = "Inventory.AttachObject"; + p.on_click.parameter = LLSD(attachment->getName()); + p.on_enable.function_name = "Attachment.Label"; + p.on_enable.parameter = cbparams; + LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu; + LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent); + items.push_back(p.name); + } + } + } + } + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +bool LLObjectBridge::renameItem(const std::string& new_name) +{ + if(!isItemRenameable()) + return false; + LLPreview::dirty(mUUID); + LLInventoryModel* model = getInventoryModel(); + if(!model) + return false; + LLViewerInventoryItem* item = getItem(); + if(item && (item->getName() != new_name)) + { + LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item->rename(new_name); + new_item->updateServer(false); + model->updateItem(new_item); + model->notifyObservers(); + buildDisplayName(); + + if (isAgentAvatarValid()) + { + LLViewerObject* obj = gAgentAvatarp->getWornAttachment( item->getUUID() ); + if(obj) + { + LLSelectMgr::getInstance()->deselectAll(); + LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, false ); + LLSelectMgr::getInstance()->selectionSetObjectName( new_name ); + LLSelectMgr::getInstance()->deselectAll(); + } + } + } + // return false because we either notified observers (& therefore + // rebuilt) or we didn't update. + return false; +} + +// +=================================================+ +// | LLLSLTextBridge | +// +=================================================+ + +void LLLSLTextBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +} + +// +=================================================+ +// | LLWearableBridge | +// +=================================================+ + +LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + LLAssetType::EType asset_type, + LLInventoryType::EType inv_type, + LLWearableType::EType wearable_type) : + LLItemBridge(inventory, root, uuid), + mAssetType( asset_type ), + mWearableType(wearable_type) +{ + mInvType = inv_type; +} + +bool LLWearableBridge::renameItem(const std::string& new_name) +{ + if (get_is_item_worn(mUUID)) + { + gAgentWearables.setWearableName( mUUID, new_name ); + } + return LLItemBridge::renameItem(new_name); +} + +std::string LLWearableBridge::getLabelSuffix() const +{ + if (get_is_item_worn(mUUID)) + { + // e.g. "(worn)" + return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn"); + } + else + { + return LLItemBridge::getLabelSuffix(); + } +} + +LLUIImagePtr LLWearableBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(mAssetType, mInvType, mWearableType, false); +} + +// virtual +void LLWearableBridge::performAction(LLInventoryModel* model, std::string action) +{ + if (isAddAction(action)) + { + wearOnAvatar(); + } + else if ("wear_add" == action) + { + wearAddOnAvatar(); + } + else if ("edit" == action) + { + editOnAvatar(); + return; + } + else if (isRemoveAction(action)) + { + removeFromAvatar(); + return; + } + else LLItemBridge::performAction(model, action); +} + +void LLWearableBridge::openItem() +{ + performAction(getInventoryModel(), + get_is_item_worn(mUUID) ? "take_off" : "wear"); +} + +void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLWearableBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + } + else + { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere + bool can_open = ((flags & SUPPRESS_OPEN_ITEM) != SUPPRESS_OPEN_ITEM); + + // If we have clothing, don't add "Open" as it's the same action as "Wear" SL-18976 + LLViewerInventoryItem* item = getItem(); + if (can_open && item) + { + can_open = (item->getType() != LLAssetType::AT_CLOTHING) && + (item->getType() != LLAssetType::AT_BODYPART); + } + if (isLinkedObjectMissing()) + { + can_open = false; + } + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + + if (can_open) + { + addOpenRightClickMenuOption(items); + } + else + { + disabled_items.push_back(std::string("Open")); + disabled_items.push_back(std::string("Open Original")); + } + + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + + items.push_back(std::string("Wearable And Object Separator")); + items.push_back(std::string("Wearable Edit")); + + if (((flags & FIRST_SELECTED_ITEM) == 0) || (item && !gAgentWearables.isWearableModifiable(item->getUUID()))) + { + disabled_items.push_back(std::string("Wearable Edit")); + } + // Don't allow items to be worn if their baseobj is in the trash. + if (isLinkedObjectInTrash() || isLinkedObjectMissing() || isCOFFolder()) + { + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); + disabled_items.push_back(std::string("Wearable Edit")); + } + + // Disable wear and take off based on whether the item is worn. + if(item) + { + switch (item->getType()) + { + case LLAssetType::AT_CLOTHING: + items.push_back(std::string("Take Off")); + // Fallthrough since clothing and bodypart share wear options + case LLAssetType::AT_BODYPART: + if (get_is_item_worn(item->getUUID())) + { + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); + } + else + { + items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Take Off")); + disabled_items.push_back(std::string("Wearable Edit")); + } + + if (LLWearableType::getInstance()->getAllowMultiwear(mWearableType)) + { + items.push_back(std::string("Wearable Add")); + if (!gAgentWearables.canAddWearable(mWearableType)) + { + disabled_items.push_back(std::string("Wearable Add")); + } + } + break; + default: + break; + } + } + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +// Called from menus +// static +bool LLWearableBridge::canWearOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return false; + if(!self->isAgentInventory()) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem(); + if(!item || !item->isFinished()) return false; + } + return (!get_is_item_worn(self->mUUID)); +} + +// Called from menus +// static +void LLWearableBridge::onWearOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return; + self->wearOnAvatar(); +} + +void LLWearableBridge::wearOnAvatar() +{ + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + LLViewerInventoryItem* item = getItem(); + if(item) + { + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); + } +} + +void LLWearableBridge::wearAddOnAvatar() +{ + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + LLViewerInventoryItem* item = getItem(); + if(item) + { + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, false); + } +} + +// static +void LLWearableBridge::onWearOnAvatarArrived( LLViewerWearable* wearable, void* userdata ) +{ + LLUUID* item_id = (LLUUID*) userdata; + if(wearable) + { + LLViewerInventoryItem* item = NULL; + item = (LLViewerInventoryItem*)gInventory.getItem(*item_id); + if(item) + { + if(item->getAssetUUID() == wearable->getAssetID()) + { + gAgentWearables.setWearableItem(item, wearable); + gInventory.notifyObservers(); + //self->getFolderItem()->refreshFromRoot(); + } + else + { + LL_INFOS() << "By the time wearable asset arrived, its inv item already pointed to a different asset." << LL_ENDL; + } + } + } + delete item_id; +} + +// static +// BAP remove the "add" code path once everything is fully COF-ified. +void LLWearableBridge::onWearAddOnAvatarArrived( LLViewerWearable* wearable, void* userdata ) +{ + LLUUID* item_id = (LLUUID*) userdata; + if(wearable) + { + LLViewerInventoryItem* item = NULL; + item = (LLViewerInventoryItem*)gInventory.getItem(*item_id); + if(item) + { + if(item->getAssetUUID() == wearable->getAssetID()) + { + bool do_append = true; + gAgentWearables.setWearableItem(item, wearable, do_append); + gInventory.notifyObservers(); + //self->getFolderItem()->refreshFromRoot(); + } + else + { + LL_INFOS() << "By the time wearable asset arrived, its inv item already pointed to a different asset." << LL_ENDL; + } + } + } + delete item_id; +} + +// static +bool LLWearableBridge::canEditOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(!self) return false; + + return (get_is_item_worn(self->mUUID)); +} + +// static +void LLWearableBridge::onEditOnAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if(self) + { + self->editOnAvatar(); + } +} + +void LLWearableBridge::editOnAvatar() +{ + LLAgentWearables::editWearable(mUUID); +} + +// static +bool LLWearableBridge::canRemoveFromAvatar(void* user_data) +{ + LLWearableBridge* self = (LLWearableBridge*)user_data; + if( self && (LLAssetType::AT_BODYPART != self->mAssetType) ) + { + return get_is_item_worn( self->mUUID ); + } + return false; +} + +void LLWearableBridge::removeFromAvatar() +{ + LL_WARNS() << "safe to remove?" << LL_ENDL; + if (get_is_item_worn(mUUID)) + { + LLAppearanceMgr::instance().removeItemFromAvatar(mUUID); + } +} + + +// +=================================================+ +// | LLLinkItemBridge | +// +=================================================+ +// For broken item links + +std::string LLLinkItemBridge::sPrefix("Link: "); + +void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // *TODO: Translate + LL_DEBUGS() << "LLLink::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + items.push_back(std::string("Find Original")); + disabled_items.push_back(std::string("Find Original")); + + if(isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Properties")); + addDeleteContextMenuOptions(items, disabled_items); + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +// +=================================================+ +// | LLSettingsBridge | +// +=================================================+ + +LLSettingsBridge::LLSettingsBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + LLSettingsType::type_e settings_type): + LLItemBridge(inventory, root, uuid), + mSettingsType(settings_type) +{ +} + +LLUIImagePtr LLSettingsBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_SETTINGS, LLInventoryType::IT_SETTINGS, mSettingsType, false); +} + +void LLSettingsBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("apply_settings_local" == action) + { + // Single item only + LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); + if (!item) + return; + LLUUID asset_id = item->getAssetUUID(); + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, asset_id, LLEnvironment::TRANSITION_INSTANT); + LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL, LLEnvironment::TRANSITION_INSTANT); + } + else if ("apply_settings_parcel" == action) + { + // Single item only + LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); + if (!item) + return; + LLUUID asset_id = item->getAssetUUID(); + std::string name = item->getName(); + + U32 flags(0); + + if (!item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) + flags |= LLSettingsBase::FLAG_NOMOD; + if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + flags |= LLSettingsBase::FLAG_NOTRANS; + + LLParcel *parcel = LLViewerParcelMgr::instance().getAgentOrSelectedParcel(); + if (!parcel) + { + LL_WARNS("INVENTORY") << "could not identify parcel." << LL_ENDL; + return; + } + S32 parcel_id = parcel->getLocalID(); + + LL_DEBUGS("ENVIRONMENT") << "Applying asset ID " << asset_id << " to parcel " << parcel_id << LL_ENDL; + LLEnvironment::instance().updateParcel(parcel_id, asset_id, name, LLEnvironment::NO_TRACK, -1, -1, flags); + LLEnvironment::instance().setSharedEnvironment(); + } + else + LLItemBridge::performAction(model, action); +} + +void LLSettingsBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + if (item->getPermissions().getOwner() != gAgent.getID()) + LLNotificationsUtil::add("NoEditFromLibrary"); + else + LLInvFVBridgeAction::doAction(item->getType(), mUUID, getInventoryModel()); + } +} + +void LLSettingsBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLSettingsBridge::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if (isMarketplaceListingsFolder()) + { + menuentry_vec_t items; + menuentry_vec_t disabled_items; + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + hide_context_entries(menu, items, disabled_items); + } + else if (isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + + addOpenRightClickMenuOption(items); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + + items.push_back("Settings Separator"); + items.push_back("Settings Apply Local"); + + items.push_back("Settings Apply Parcel"); + if (!canUpdateParcel()) + disabled_items.push_back("Settings Apply Parcel"); + + items.push_back("Settings Apply Region"); + if (!canUpdateRegion()) + disabled_items.push_back("Settings Apply Region"); + } + addLinkReplaceMenuOption(items, disabled_items); + hide_context_entries(menu, items, disabled_items); +} + +bool LLSettingsBridge::renameItem(const std::string& new_name) +{ + /*TODO: change internal settings name? */ + return LLItemBridge::renameItem(new_name); +} + +bool LLSettingsBridge::isItemRenameable() const +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + return (item->getPermissions().allowModifyBy(gAgent.getID())); + } + return false; +} + +bool LLSettingsBridge::canUpdateParcel() const +{ + return LLEnvironment::instance().canAgentUpdateParcelEnvironment(); +} + +bool LLSettingsBridge::canUpdateRegion() const +{ + return LLEnvironment::instance().canAgentUpdateRegionEnvironment(); +} + + +// +=================================================+ +// | LLMaterialBridge | +// +=================================================+ + +void LLMaterialBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); + } +} + +void LLMaterialBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLMaterialBridge::buildContextMenu()" << LL_ENDL; + + if (isMarketplaceListingsFolder()) + { + menuentry_vec_t items; + menuentry_vec_t disabled_items; + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + hide_context_entries(menu, items, disabled_items); + } + else + { + LLItemBridge::buildContextMenu(menu, flags); + } +} + + +// +=================================================+ +// | LLLinkBridge | +// +=================================================+ +// For broken folder links. +std::string LLLinkFolderBridge::sPrefix("Link: "); +LLUIImagePtr LLLinkFolderBridge::getIcon() const +{ + LLFolderType::EType folder_type = LLFolderType::FT_NONE; + const LLInventoryObject *obj = getInventoryObject(); + if (obj) + { + LLViewerInventoryCategory* cat = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + cat = (LLViewerInventoryCategory*)model->getCategory(obj->getLinkedUUID()); + if (cat) + { + folder_type = cat->getPreferredType(); + } + } + } + return LLFolderBridge::getIcon(folder_type); +} + +void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // *TODO: Translate + LL_DEBUGS() << "LLLink::buildContextMenu()" << LL_ENDL; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if (isItemInTrash()) + { + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Find Original")); + addDeleteContextMenuOptions(items, disabled_items); + } + hide_context_entries(menu, items, disabled_items); +} +void LLLinkFolderBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("goto" == action) + { + gotoItem(); + return; + } + LLItemBridge::performAction(model,action); +} +void LLLinkFolderBridge::gotoItem() +{ + LLItemBridge::gotoItem(); + + const LLUUID &cat_uuid = getFolderID(); + if (!cat_uuid.isNull()) + { + LLFolderViewItem *base_folder = LLInventoryPanel::getActiveInventoryPanel()->getItemByID(cat_uuid); + if (base_folder) + { + base_folder->setOpen(true); + } + } +} +const LLUUID &LLLinkFolderBridge::getFolderID() const +{ + if (LLViewerInventoryItem *link_item = getItem()) + { + if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory()) + { + const LLUUID& cat_uuid = cat->getUUID(); + return cat_uuid; + } + } + return LLUUID::null; +} + +void LLUnknownItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + items.push_back(std::string("Properties")); + items.push_back(std::string("Rename")); + hide_context_entries(menu, items, disabled_items); +} + +LLUIImagePtr LLUnknownItemBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_UNKNOWN, mInvType); +} + + +/******************************************************************************** + ** + ** BRIDGE ACTIONS + **/ + +// static +void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type, + const LLUUID& uuid, + LLInventoryModel* model) +{ + // Perform indirection in case of link. + const LLUUID& linked_uuid = gInventory.getLinkedItemID(uuid); + + LLInvFVBridgeAction* action = createAction(asset_type,linked_uuid,model); + if(action) + { + action->doIt(); + delete action; + } +} + +// static +void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model) +{ + llassert(model); + LLViewerInventoryItem* item = model->getItem(uuid); + llassert(item); + if (item) + { + LLAssetType::EType asset_type = item->getType(); + LLInvFVBridgeAction* action = createAction(asset_type,uuid,model); + if(action) + { + action->doIt(); + delete action; + } + } +} + +LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const +{ + if (mModel) + return (LLViewerInventoryItem*)mModel->getItem(mUUID); + return NULL; +} + +class LLTextureBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + if (getItem()) + { + LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLTextureBridgeAction(){} +protected: + LLTextureBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLSoundBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + send_sound_trigger(item->getAssetUUID(), SOUND_GAIN); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLSoundBridgeAction(){} +protected: + LLSoundBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLLandmarkBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + // Opening (double-clicking) a landmark immediately teleports, + // but warns you the first time. + LLSD payload; + payload["asset_id"] = item->getAssetUUID(); + + LLSD args; + args["LOCATION"] = item->getName(); + + LLNotificationsUtil::add("TeleportFromLandmark", args, payload); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLLandmarkBridgeAction(){} +protected: + LLLandmarkBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLCallingCardBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item && item->getCreatorUUID().notNull()) + { + LLAvatarActions::showProfile(item->getCreatorUUID()); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLCallingCardBridgeAction(){} +protected: + LLCallingCardBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} + +}; + +class LLNotecardBridgeAction +: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLNotecardBridgeAction(){} +protected: + LLNotecardBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLGestureBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null); + preview->setFocus(true); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLGestureBridgeAction(){} +protected: + LLGestureBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLAnimationBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLAnimationBridgeAction(){} +protected: + LLAnimationBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLObjectBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + attachOrDetach(); + } + virtual ~LLObjectBridgeAction(){} +protected: + LLObjectBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} + void attachOrDetach(); +}; + +void LLObjectBridgeAction::attachOrDetach() +{ + if (get_is_item_worn(mUUID)) + { + LLAppearanceMgr::instance().removeItemFromAvatar(mUUID); + } + else + { + LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. + } +} + +class LLLSLTextBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES); + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLLSLTextBridgeAction(){} +protected: + LLLSLTextBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + +class LLWearableBridgeAction: public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + wearOnAvatar(); + } + + virtual ~LLWearableBridgeAction(){} +protected: + LLWearableBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} + bool isItemInTrash() const; + // return true if the item is in agent inventory. if false, it + // must be lost or in the inventory library. + bool isAgentInventory() const; + void wearOnAvatar(); +}; + +bool LLWearableBridgeAction::isItemInTrash() const +{ + if(!mModel) return false; + const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH); + return mModel->isObjectDescendentOf(mUUID, trash_id); +} + +bool LLWearableBridgeAction::isAgentInventory() const +{ + if(!mModel) return false; + if(gInventory.getRootFolderID() == mUUID) return true; + return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()); +} + +void LLWearableBridgeAction::wearOnAvatar() +{ + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + LLViewerInventoryItem* item = getItem(); + if(item) + { + if (get_is_item_worn(mUUID)) + { + if(item->getType() != LLAssetType::AT_BODYPART) + { + LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID()); + } + } + else + { + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); + } + } +} + +class LLSettingsBridgeAction + : public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + virtual void doIt() + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLSettingsType::type_e type = item->getSettingsType(); + switch (type) + { + case LLSettingsType::ST_SKY: + LLFloaterReg::showInstance("env_fixed_environmentent_sky", LLSDMap("inventory_id", item->getUUID()), TAKE_FOCUS_YES); + break; + case LLSettingsType::ST_WATER: + LLFloaterReg::showInstance("env_fixed_environmentent_water", LLSDMap("inventory_id", item->getUUID()), TAKE_FOCUS_YES); + break; + case LLSettingsType::ST_DAYCYCLE: + LLFloaterReg::showInstance("env_edit_extdaycycle", LLSDMap("inventory_id", item->getUUID())("edit_context", "inventory"), TAKE_FOCUS_YES); + break; + default: + break; + } + } + LLInvFVBridgeAction::doIt(); + } + virtual ~LLSettingsBridgeAction(){} +protected: + LLSettingsBridgeAction(const LLUUID& id, LLInventoryModel* model) : LLInvFVBridgeAction(id, model) {} +}; + +class LLMaterialBridgeAction : public LLInvFVBridgeAction +{ + friend class LLInvFVBridgeAction; +public: + void doIt() override + { + LLViewerInventoryItem* item = getItem(); + if (item) + { + LLFloaterReg::showInstance("material_editor", LLSD(item->getUUID()), TAKE_FOCUS_YES); + } + LLInvFVBridgeAction::doIt(); + } + ~LLMaterialBridgeAction() = default; +private: + LLMaterialBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +}; + + +LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type, + const LLUUID& uuid, + LLInventoryModel* model) +{ + LLInvFVBridgeAction* action = NULL; + switch(asset_type) + { + case LLAssetType::AT_TEXTURE: + action = new LLTextureBridgeAction(uuid,model); + break; + case LLAssetType::AT_SOUND: + action = new LLSoundBridgeAction(uuid,model); + break; + case LLAssetType::AT_LANDMARK: + action = new LLLandmarkBridgeAction(uuid,model); + break; + case LLAssetType::AT_CALLINGCARD: + action = new LLCallingCardBridgeAction(uuid,model); + break; + case LLAssetType::AT_OBJECT: + action = new LLObjectBridgeAction(uuid,model); + break; + case LLAssetType::AT_NOTECARD: + action = new LLNotecardBridgeAction(uuid,model); + break; + case LLAssetType::AT_ANIMATION: + action = new LLAnimationBridgeAction(uuid,model); + break; + case LLAssetType::AT_GESTURE: + action = new LLGestureBridgeAction(uuid,model); + break; + case LLAssetType::AT_LSL_TEXT: + action = new LLLSLTextBridgeAction(uuid,model); + break; + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + action = new LLWearableBridgeAction(uuid,model); + break; + case LLAssetType::AT_SETTINGS: + action = new LLSettingsBridgeAction(uuid, model); + break; + case LLAssetType::AT_MATERIAL: + action = new LLMaterialBridgeAction(uuid, model); + break; + default: + break; + } + return action; +} + +/** Bridge Actions + ** + ********************************************************************************/ + +/************************************************************************/ +/* Recent Inventory Panel related classes */ +/************************************************************************/ +void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t disabled_items, items; + buildContextMenuOptions(flags, items, disabled_items); + + items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); + + hide_context_entries(menu, items, disabled_items); +} + +LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( + LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, + LLFolderView* root, + const LLUUID& uuid, + U32 flags /*= 0x00*/ ) const +{ + LLInvFVBridge* new_listener = NULL; + if (asset_type == LLAssetType::AT_CATEGORY + && actual_asset_type != LLAssetType::AT_LINK_FOLDER) + { + new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid); + } + else + { + new_listener = LLInventoryFolderViewModelBuilder::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + view_model, + root, + uuid, + flags); + } + return new_listener; +} + +LLFolderViewGroupedItemBridge::LLFolderViewGroupedItemBridge() +{ +} + +void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) +{ + uuid_vec_t ids; + menuentry_vec_t disabled_items; + if (get_selection_item_uuids(selected_items, ids)) + { + if (!LLAppearanceMgr::instance().canAddWearables(ids) && canWearSelected(ids)) + { + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); + disabled_items.push_back(std::string("Attach To")); + disabled_items.push_back(std::string("Attach To HUD")); + } + } + disable_context_entries_if_present(menu, disabled_items); +} + +bool LLFolderViewGroupedItemBridge::canWearSelected(const uuid_vec_t& item_ids) const +{ + for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) + { + const LLViewerInventoryItem* item = gInventory.getItem(*it); + if (!item || (item->getType() >= LLAssetType::AT_COUNT) || (item->getType() <= LLAssetType::AT_NONE)) + { + return false; + } + } + return true; +} +// EOF |