phoenix-firestorm/indra/newview/fsfloaterwearablefavorites.cpp

393 lines
13 KiB
C++

/**
* @file fsfloaterwearablefavorites.cpp
* @brief Class for the favorite wearables floater
*
* $LicenseInfo:firstyear=2018&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (c) 2018 Ansariel Hiller @ Second Life
*
* 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
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "fsfloaterwearablefavorites.h"
#include "fscommon.h"
#include "llappearancemgr.h"
#include "llbutton.h"
#include "llfiltereditor.h"
#include "llinventoryfunctions.h"
#include "llinventoryobserver.h"
#include "llmenugl.h"
#include "llmenubutton.h"
#include "lltoggleablemenu.h"
#include "llviewercontrol.h" // for gSavedSettings
#include "llviewermenu.h" // for gMenuHolder
#include "rlvactions.h"
#include "rlvlocks.h"
// Hello Kokua! Have fun yoinking! ;)
#define FS_WEARABLE_FAVORITES_FOLDER "#Wearable Favorites"
static LLDefaultChildRegistry::Register<FSWearableFavoritesItemsList> r("fs_wearable_favorites_items_list");
FSWearableFavoritesItemsList::FSWearableFavoritesItemsList(const Params& p)
: LLWearableItemsList(p)
{
}
bool FSWearableFavoritesItemsList::handleDragAndDrop(S32 x, S32 y, MASK mask,
bool drop,
EDragAndDropType cargo_type,
void* cargo_data,
EAcceptance* accept,
std::string& tooltip_msg)
{
// Scroll folder view if needed. Never accepts a drag or drop.
*accept = ACCEPT_NO;
autoScroll(x, y);
if (cargo_type == DAD_BODYPART || cargo_type == DAD_CLOTHING || cargo_type == DAD_OBJECT)
{
if (drop)
{
LLInventoryItem* item = (LLInventoryItem*)cargo_data;
if (!mDADSignal.empty())
{
mDADSignal(item->getUUID());
}
}
else
{
*accept = ACCEPT_YES_SINGLE;
}
}
return true;
}
LLUUID FSFloaterWearableFavorites::sFolderID = LLUUID();
FSFloaterWearableFavorites::FSFloaterWearableFavorites(const LLSD& key)
: LLFloater(key),
mItemsList(nullptr),
mInitialized(false),
mDADCallbackConnection()
{
mCategoriesObserver = new LLInventoryCategoriesObserver();
}
FSFloaterWearableFavorites::~FSFloaterWearableFavorites()
{
if (gInventory.containsObserver(mCategoriesObserver))
{
gInventory.removeObserver(mCategoriesObserver);
}
delete mCategoriesObserver;
if (mDADCallbackConnection.connected())
{
mDADCallbackConnection.disconnect();
}
if (mOptionsMenuHandle.get())
{
mOptionsMenuHandle.get()->die();
}
}
//virtual
bool FSFloaterWearableFavorites::postBuild()
{
mItemsList = getChild<FSWearableFavoritesItemsList>("favorites_list");
mItemsList->setNoFilteredItemsMsg(getString("search_no_items"));
mItemsList->setDoubleClickCallback(boost::bind(&FSFloaterWearableFavorites::onDoubleClick, this));
mRemoveItemBtn = getChild<LLButton>("remove_btn");
mRemoveItemBtn->setCommitCallback(boost::bind(&FSFloaterWearableFavorites::handleRemove, this));
mFilterEditor = getChild<LLFilterEditor>("wearable_filter_input");
mFilterEditor->setCommitCallback(boost::bind(&FSFloaterWearableFavorites::onFilterEdit, this, _2));
// Create menus.
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
registrar.add("FavWearables.Action", boost::bind(&FSFloaterWearableFavorites::onOptionsMenuItemClicked, this, _2));
enable_registrar.add("FavWearables.CheckAction", boost::bind(&FSFloaterWearableFavorites::onOptionsMenuItemChecked, this, _2));
mOptionsButton = getChild<LLMenuButton>("options_btn");
if (LLToggleableMenu* options_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_fs_wearable_favorites.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); options_menu)
{
mOptionsMenuHandle = options_menu->getHandle();
mOptionsButton->setMenu(options_menu, LLMenuButton::MP_BOTTOM_LEFT);
}
return true;
}
//virtual
void FSFloaterWearableFavorites::onOpen(const LLSD& /*info*/)
{
if (!mInitialized)
{
if (sFolderID.isNull())
{
initCategory(boost::bind(&FSFloaterWearableFavorites::initialize, this));
}
else
{
initialize();
}
}
}
void FSFloaterWearableFavorites::initialize()
{
LLViewerInventoryCategory* category = gInventory.getCategory(sFolderID);
if (!category)
{
return;
}
const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
LLViewerInventoryCategory* category_cof = gInventory.getCategory(cof);
if (!category_cof)
{
return;
}
gInventory.addObserver(mCategoriesObserver);
mCategoriesObserver->addCategory(sFolderID, boost::bind(&FSFloaterWearableFavorites::updateList, this, sFolderID));
mCategoriesObserver->addCategory(cof, boost::bind(&FSFloaterWearableFavorites::updateList, this, sFolderID));
category->fetch();
mItemsList->setSortOrder((LLWearableItemsList::ESortOrder)gSavedSettings.getU32("FSWearableFavoritesSortOrder"));
updateList(sFolderID);
mItemsList->setDADCallback(boost::bind(&FSFloaterWearableFavorites::onItemDAD, this, _1));
mInitialized = true;
}
//virtual
void FSFloaterWearableFavorites::draw()
{
LLFloater::draw();
mRemoveItemBtn->setEnabled(mItemsList->numSelected() > 0);
}
//virtual
bool FSFloaterWearableFavorites::handleKeyHere(KEY key, MASK mask)
{
if (FSCommon::isFilterEditorKeyCombo(key, mask))
{
mFilterEditor->setFocus(true);
return true;
}
return LLFloater::handleKeyHere(key, mask);
}
// static
std::optional<LLUUID> FSFloaterWearableFavorites::getWearableFavoritesFolderID()
{
if (LLUUID fs_root_cat_id = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER); !fs_root_cat_id.isNull())
{
LLInventoryModel::item_array_t* items;
LLInventoryModel::cat_array_t* cats;
gInventory.getDirectDescendentsOf(fs_root_cat_id, cats, items);
if (cats)
{
for (const auto& cat : *cats)
{
if (cat->getName() == FS_WEARABLE_FAVORITES_FOLDER)
{
return cat->getUUID();
}
}
}
}
return std::nullopt;
}
// static
void FSFloaterWearableFavorites::initCategory(inventory_func_type callback)
{
if (!gInventory.isInventoryUsable())
{
LL_WARNS() << "Cannot initialize Favorite Wearables inventory folder - inventory is not usable!" << LL_ENDL;
return;
}
if (auto fs_favs_id = getWearableFavoritesFolderID(); fs_favs_id.has_value())
{
sFolderID = fs_favs_id.value();
callback(sFolderID);
}
else
{
LLUUID fs_root_cat_id = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER);
if (fs_root_cat_id.isNull())
{
gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER, [callback](const LLUUID& new_cat_id)
{
gInventory.createNewCategory(new_cat_id, LLFolderType::FT_NONE, FS_WEARABLE_FAVORITES_FOLDER, [callback](const LLUUID& new_cat_id)
{
FSFloaterWearableFavorites::sFolderID = new_cat_id;
callback(new_cat_id);
});
});
}
else
{
gInventory.createNewCategory(fs_root_cat_id, LLFolderType::FT_NONE, FS_WEARABLE_FAVORITES_FOLDER, [callback](const LLUUID& new_cat_id)
{
FSFloaterWearableFavorites::sFolderID = new_cat_id;
callback(new_cat_id);
});
}
}
}
//static
LLUUID FSFloaterWearableFavorites::getFavoritesFolder()
{
if (!sFolderID.isNull())
{
if (auto fs_favs_id = getWearableFavoritesFolderID(); fs_favs_id.has_value())
{
sFolderID = fs_favs_id.value();
}
}
return sFolderID;
}
void FSFloaterWearableFavorites::updateList(const LLUUID& folder_id)
{
mItemsList->updateList(folder_id);
if (gInventory.isCategoryComplete(folder_id))
{
mItemsList->setNoItemsCommentText(getString("empty_list")); // Have to reset it here because LLWearableItemsList::updateList might override it
}
}
void FSFloaterWearableFavorites::onItemDAD(const LLUUID& item_id)
{
link_inventory_object(sFolderID, item_id, LLPointer<LLInventoryCallback>(nullptr));
}
void FSFloaterWearableFavorites::handleRemove()
{
uuid_vec_t selected_item_ids;
mItemsList->getSelectedUUIDs(selected_item_ids);
for (const auto& id : selected_item_ids)
{
remove_inventory_item(id, LLPointer<LLInventoryCallback>(nullptr));
}
}
void FSFloaterWearableFavorites::onFilterEdit(const std::string& search_string)
{
mItemsList->setFilterSubString(search_string, true);
mItemsList->setNoItemsCommentText(getString("empty_list"));
mItemsList->rearrange();
}
void FSFloaterWearableFavorites::onOptionsMenuItemClicked(const LLSD& userdata)
{
const std::string action = userdata.asString();
if (action == "sort_by_name")
{
mItemsList->setSortOrder(LLWearableItemsList::E_SORT_BY_NAME);
gSavedSettings.setU32("FSWearableFavoritesSortOrder", LLWearableItemsList::E_SORT_BY_NAME);
}
else if (action == "sort_by_most_recent")
{
mItemsList->setSortOrder(LLWearableItemsList::E_SORT_BY_MOST_RECENT);
gSavedSettings.setU32("FSWearableFavoritesSortOrder", LLWearableItemsList::E_SORT_BY_MOST_RECENT);
}
else if (action == "sort_by_type_name")
{
mItemsList->setSortOrder(LLWearableItemsList::E_SORT_BY_TYPE_NAME);
gSavedSettings.setU32("FSWearableFavoritesSortOrder", LLWearableItemsList::E_SORT_BY_TYPE_NAME);
}
}
bool FSFloaterWearableFavorites::onOptionsMenuItemChecked(const LLSD& userdata)
{
const std::string action = userdata.asString();
if (action == "sort_by_name")
{
return mItemsList->getSortOrder() == LLWearableItemsList::E_SORT_BY_NAME;
}
else if (action == "sort_by_most_recent")
{
return mItemsList->getSortOrder() == LLWearableItemsList::E_SORT_BY_MOST_RECENT;
}
else if (action == "sort_by_type_name")
{
return mItemsList->getSortOrder() == LLWearableItemsList::E_SORT_BY_TYPE_NAME;
}
return false;
}
void FSFloaterWearableFavorites::onDoubleClick()
{
if (LLUUID selected_item_id = mItemsList->getSelectedUUID(); selected_item_id.notNull())
{
uuid_vec_t ids;
ids.push_back(selected_item_id);
LLViewerInventoryItem* item = gInventory.getItem(selected_item_id);
if (get_is_item_worn(selected_item_id))
{
if ((item->getType() == LLAssetType::AT_CLOTHING && (!RlvActions::isRlvEnabled() || gRlvWearableLocks.canRemove(item))) ||
((item->getType() == LLAssetType::AT_OBJECT) && (!RlvActions::isRlvEnabled() || gRlvAttachmentLocks.canDetach(item))))
{
LLAppearanceMgr::instance().removeItemsFromAvatar(ids);
}
}
else
{
if (item->getType() == LLAssetType::AT_BODYPART && (!RlvActions::isRlvEnabled() || (gRlvWearableLocks.canWear(item) & RLV_WEAR_REPLACE) == RLV_WEAR_REPLACE))
{
wear_multiple(ids, true);
}
else if (item->getType() == LLAssetType::AT_CLOTHING && LLAppearanceMgr::instance().canAddWearables(ids) && (!RlvActions::isRlvEnabled() || (gRlvWearableLocks.canWear(item) & RLV_WEAR_ADD) == RLV_WEAR_ADD))
{
wear_multiple(ids, false);
}
else if (item->getType() == LLAssetType::AT_OBJECT && LLAppearanceMgr::instance().canAddWearables(ids) && (!RlvActions::isRlvEnabled() || (gRlvAttachmentLocks.canAttach(item) & RLV_WEAR_ADD) == RLV_WEAR_ADD))
{
wear_multiple(ids, false);
}
}
}
}