/**
 * @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, &copy_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 &current_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 &current_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 &current_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 &current_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 &current_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