From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/newview/lloutfitslist.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/lloutfitslist.cpp') diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 5c7792b0df..b445f0c04e 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -109,7 +109,7 @@ LLOutfitsList::~LLOutfitsList() { } -BOOL LLOutfitsList::postBuild() +bool LLOutfitsList::postBuild() { mAccordion = getChild("outfits_accordion"); mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); @@ -982,7 +982,7 @@ void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LL signalSelectionOutfitUUID(category_id); } -BOOL LLOutfitListBase::postBuild() +bool LLOutfitListBase::postBuild() { mGearMenu = createGearMenu(); @@ -990,7 +990,7 @@ BOOL LLOutfitListBase::postBuild() menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenuBase::updateItemsVisibility, mGearMenu)); menu_gear_btn->setMenu(mGearMenu->getMenu()); - return TRUE; + return true; } void LLOutfitListBase::collapseAllFolders() -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/lloutfitslist.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'indra/newview/lloutfitslist.cpp') diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index b445f0c04e..0840f82e80 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -279,11 +279,11 @@ void LLOutfitListBase::performAction(std::string action) if ("replaceoutfit" == action) { - LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, FALSE ); + LLAppearanceMgr::instance().wearInventoryCategory( cat, false, false ); } else if ("addtooutfit" == action) { - LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, TRUE ); + LLAppearanceMgr::instance().wearInventoryCategory( cat, false, true ); } else if ("rename_outfit" == action) { @@ -305,7 +305,7 @@ void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); if (!list) continue; - tab->setFocus(TRUE); + tab->setFocus(true); ChangeOutfitSelection(list, outfit_uuid); tab->changeOpenClose(false); @@ -451,7 +451,7 @@ void LLOutfitsList::resetItemSelection(LLWearableItemsList* list, const LLUUID& void LLOutfitsList::onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) { - MASK mask = gKeyboard->currentMask(TRUE); + MASK mask = gKeyboard->currentMask(true); // Reset selection in all previously selected tabs except for the current // if new selection is started. @@ -532,7 +532,7 @@ void LLOutfitsList::applyFilter(const std::string& new_filter_substring) // to compare it with updated string if it was previously hidden. if (!more_restrictive) { - tab->setVisible(TRUE); + tab->setVisible(true); } LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); @@ -757,7 +757,7 @@ void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUI LLUICtrl* header = tab->findChild("dd_header"); if (header) { - header->setFocus(TRUE); + header->setFocus(true); } uuid_vec_t selected_uuids; @@ -1167,7 +1167,7 @@ void LLOutfitListGearMenuBase::onWear() if (selected_outfit) { LLAppearanceMgr::instance().wearInventoryCategory( - selected_outfit, /*copy=*/ FALSE, /*append=*/ FALSE); + selected_outfit, /*copy=*/ false, /*append=*/ false); } } @@ -1268,11 +1268,11 @@ LLOutfitListGearMenu::~LLOutfitListGearMenu() void LLOutfitListGearMenu::onUpdateItemsVisibility() { if (!mMenu) return; - mMenu->setItemVisible("expand", TRUE); - mMenu->setItemVisible("collapse", TRUE); - mMenu->setItemVisible("thumbnail", FALSE); // Never visible? - mMenu->setItemVisible("sepatator3", FALSE); - mMenu->setItemVisible("sort_folders_by_name", FALSE); + mMenu->setItemVisible("expand", true); + mMenu->setItemVisible("collapse", true); + mMenu->setItemVisible("thumbnail", false); // Never visible? + mMenu->setItemVisible("sepatator3", false); + mMenu->setItemVisible("sort_folders_by_name", false); LLOutfitListGearMenuBase::onUpdateItemsVisibility(); } -- cgit v1.2.3 From 5f4d312c8d2b6ba0fd13279ccfc569acd4f37c82 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 24 Apr 2024 19:49:27 +0200 Subject: Fix BOOL vs bool issues after merge --- indra/newview/lloutfitslist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/lloutfitslist.cpp') diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index a7f9f53b9f..060f10d813 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -1356,7 +1356,7 @@ bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) .delay_time(LLView::getTooltipTimeout()) .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) .create_params(params)); - return TRUE; + return true; } return LLAccordionCtrlTab::handleToolTip(x, y, mask); -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/lloutfitslist.cpp | 2738 +++++++++++++++++++-------------------- 1 file changed, 1369 insertions(+), 1369 deletions(-) (limited to 'indra/newview/lloutfitslist.cpp') diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 665cfba9e6..94b32ceea9 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -1,1369 +1,1369 @@ -/** - * @file lloutfitslist.cpp - * @brief List of agent's outfits for My Appearance side panel. - * - * $LicenseInfo:firstyear=2010&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 "lloutfitslist.h" - -// llcommon -#include "llcommonutils.h" - -#include "llaccordionctrl.h" -#include "llaccordionctrltab.h" -#include "llagentwearables.h" -#include "llappearancemgr.h" -#include "llfloaterreg.h" -#include "llfloatersidepanelcontainer.h" -#include "llinspecttexture.h" -#include "llinventoryfunctions.h" -#include "llinventorymodel.h" -#include "llmenubutton.h" -#include "llnotificationsutil.h" -#include "lloutfitobserver.h" -#include "lltoggleablemenu.h" -#include "lltransutil.h" -#include "llviewermenu.h" -#include "llvoavatar.h" -#include "llvoavatarself.h" -#include "llwearableitemslist.h" - -static bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y); - -static const LLOutfitTabNameComparator OUTFIT_TAB_NAME_COMPARATOR; - -/*virtual*/ -bool LLOutfitTabNameComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const -{ - std::string name1 = tab1->getTitle(); - std::string name2 = tab2->getTitle(); - - return (LLStringUtil::compareDict(name1, name2) < 0); -} - -struct outfit_accordion_tab_params : public LLInitParam::Block -{ - Mandatory wearable_list; - - outfit_accordion_tab_params() - : wearable_list("wearable_items_list") - {} -}; - -const outfit_accordion_tab_params& get_accordion_tab_params() -{ - static outfit_accordion_tab_params tab_params; - static bool initialized = false; - if (!initialized) - { - initialized = true; - - LLXMLNodePtr xmlNode; - if (LLUICtrlFactory::getLayeredXMLNode("outfit_accordion_tab.xml", xmlNode)) - { - LLXUIParser parser; - parser.readXUI(xmlNode, tab_params, "outfit_accordion_tab.xml"); - } - else - { - LL_WARNS() << "Failed to read xml of Outfit's Accordion Tab from outfit_accordion_tab.xml" << LL_ENDL; - } - } - - return tab_params; -} - - -static LLPanelInjector t_outfits_list("outfits_list"); - -LLOutfitsList::LLOutfitsList() - : LLOutfitListBase() - , mAccordion(NULL) - , mListCommands(NULL) - , mItemSelected(false) -{ -} - -LLOutfitsList::~LLOutfitsList() -{ -} - -bool LLOutfitsList::postBuild() -{ - mAccordion = getChild("outfits_accordion"); - mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); - - return LLOutfitListBase::postBuild(); -} - -//virtual -void LLOutfitsList::onOpen(const LLSD& info) -{ - if (!mIsInitialized) - { - // Start observing changes in Current Outfit category. - LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLOutfitsList::onCOFChanged, this)); - } - - LLOutfitListBase::onOpen(info); - - LLAccordionCtrlTab* selected_tab = mAccordion->getSelectedTab(); - if (!selected_tab) return; - - // Pass focus to the selected outfit tab. - selected_tab->showAndFocusHeader(); -} - - -void LLOutfitsList::updateAddedCategory(LLUUID cat_id) -{ - LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); - if (!cat) return; - - std::string name = cat->getName(); - - outfit_accordion_tab_params tab_params(get_accordion_tab_params()); - tab_params.cat_id = cat_id; - LLOutfitAccordionCtrlTab *tab = LLUICtrlFactory::create(tab_params); - if (!tab) return; - LLWearableItemsList* wearable_list = LLUICtrlFactory::create(tab_params.wearable_list); - wearable_list->setShape(tab->getLocalRect()); - tab->addChild(wearable_list); - - tab->setName(name); - tab->setTitle(name); - - // *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated. - tab->setDisplayChildren(false); - mAccordion->addCollapsibleCtrl(tab); - - // Start observing the new outfit category. - LLWearableItemsList* list = tab->getChild("wearable_items_list"); - if (!mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id))) - { - // Remove accordion tab if category could not be added to observer. - mAccordion->removeCollapsibleCtrl(tab); - - // kill removed tab - tab->die(); - return; - } - - // Map the new tab with outfit category UUID. - mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab)); - - tab->setRightMouseDownCallback(boost::bind(&LLOutfitListBase::outfitRightClickCallBack, this, - _1, _2, _3, cat_id)); - - // Setting tab focus callback to monitor currently selected outfit. - tab->setFocusReceivedCallback(boost::bind(&LLOutfitListBase::ChangeOutfitSelection, this, list, cat_id)); - - // Setting callback to reset items selection inside outfit on accordion collapsing and expanding (EXT-7875) - tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::resetItemSelection, this, list, cat_id)); - - // force showing list items that don't match current filter(EXT-7158) - list->setForceShowingUnmatchedItems(true); - - // Setting list commit callback to monitor currently selected wearable item. - list->setCommitCallback(boost::bind(&LLOutfitsList::onListSelectionChange, this, _1)); - - // Setting list refresh callback to apply filter on list change. - list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onRefreshComplete, this, _1)); - - list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3)); - - // Fetch the new outfit contents. - cat->fetch(); - - // Refresh the list of outfit items after fetch(). - // Further list updates will be triggered by the category observer. - list->updateList(cat_id); - - // If filter is currently applied we store the initial tab state. - if (!getFilterSubString().empty()) - { - tab->notifyChildren(LLSD().with("action", "store_state")); - - // Setting mForceRefresh flag will make the list refresh its contents - // even if it is not currently visible. This is required to apply the - // filter to the newly added list. - list->setForceRefresh(true); - - list->setFilterSubString(getFilterSubString(), false); - } -} - -void LLOutfitsList::updateRemovedCategory(LLUUID cat_id) -{ - outfits_map_t::iterator outfits_iter = mOutfitsMap.find(cat_id); - if (outfits_iter != mOutfitsMap.end()) - { - const LLUUID& outfit_id = outfits_iter->first; - LLAccordionCtrlTab* tab = outfits_iter->second; - - // An outfit is removed from the list. Do the following: - // 1. Remove outfit category from observer to stop monitoring its changes. - mCategoriesObserver->removeCategory(outfit_id); - - // 2. Remove the outfit from selection. - deselectOutfit(outfit_id); - - // 3. Remove category UUID to accordion tab mapping. - mOutfitsMap.erase(outfits_iter); - - // 4. Remove outfit tab from accordion. - mAccordion->removeCollapsibleCtrl(tab); - - // kill removed tab - if (tab != NULL) - { - tab->die(); - } - } -} - -//virtual -void LLOutfitsList::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id) -{ - if (mOutfitsMap[prev_id]) - { - mOutfitsMap[prev_id]->setTitleFontStyle("NORMAL"); - mOutfitsMap[prev_id]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor")); - } - if (mOutfitsMap[base_id]) - { - mOutfitsMap[base_id]->setTitleFontStyle("BOLD"); - mOutfitsMap[base_id]->setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor")); - } -} - -void LLOutfitsList::onListSelectionChange(LLUICtrl* ctrl) -{ - LLWearableItemsList* list = dynamic_cast(ctrl); - if (!list) return; - - LLViewerInventoryItem *item = gInventory.getItem(list->getSelectedUUID()); - if (!item) return; - - ChangeOutfitSelection(list, item->getParentUUID()); -} - -void LLOutfitListBase::performAction(std::string action) -{ - if (mSelectedOutfitUUID.isNull()) return; - - LLViewerInventoryCategory* cat = gInventory.getCategory(mSelectedOutfitUUID); - if (!cat) return; - - if ("replaceoutfit" == action) - { - LLAppearanceMgr::instance().wearInventoryCategory( cat, false, false ); - } - else if ("addtooutfit" == action) - { - LLAppearanceMgr::instance().wearInventoryCategory( cat, false, true ); - } - else if ("rename_outfit" == action) - { - LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID); - } -} - -void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) -{ - for (outfits_map_t::iterator iter = mOutfitsMap.begin(); - iter != mOutfitsMap.end(); - ++iter) - { - if (outfit_uuid == iter->first) - { - LLAccordionCtrlTab* tab = iter->second; - if (!tab) continue; - - LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); - if (!list) continue; - - tab->setFocus(true); - ChangeOutfitSelection(list, outfit_uuid); - - tab->changeOpenClose(false); - } - } -} - -// virtual -bool LLOutfitListBase::isActionEnabled(const LLSD& userdata) -{ - if (mSelectedOutfitUUID.isNull()) return false; - - const std::string command_name = userdata.asString(); - if (command_name == "delete") - { - return !hasItemSelected() && LLAppearanceMgr::instance().getCanRemoveOutfit(mSelectedOutfitUUID); - } - if (command_name == "rename") - { - return get_is_category_renameable(&gInventory, mSelectedOutfitUUID); - } - if (command_name == "save_outfit") - { - bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); - bool outfit_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); - // allow save only if outfit isn't locked and is dirty - return !outfit_locked && outfit_dirty; - } - if (command_name == "wear") - { - if (gAgentWearables.isCOFChangeInProgress()) - { - return false; - } - - if (hasItemSelected()) - { - return canWearSelected(); - } - - // outfit selected - return LLAppearanceMgr::instance().getCanReplaceCOF(mSelectedOutfitUUID); - } - if (command_name == "take_off") - { - // Enable "Take Off" if any of selected items can be taken off - // or the selected outfit contains items that can be taken off. - return ( hasItemSelected() && canTakeOffSelected() ) - || ( !hasItemSelected() && LLAppearanceMgr::getCanRemoveFromCOF(mSelectedOutfitUUID) ); - } - - if (command_name == "wear_add") - { - // *TODO: do we ever get here? - return LLAppearanceMgr::getCanAddToCOF(mSelectedOutfitUUID); - } - - return false; -} - -void LLOutfitsList::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const -{ - // Collect selected items from all selected lists. - for (wearables_lists_map_t::const_iterator iter = mSelectedListsMap.begin(); - iter != mSelectedListsMap.end(); - ++iter) - { - uuid_vec_t uuids; - (*iter).second->getSelectedUUIDs(uuids); - - S32 prev_size = selected_uuids.size(); - selected_uuids.resize(prev_size + uuids.size()); - std::copy(uuids.begin(), uuids.end(), selected_uuids.begin() + prev_size); - } -} - -void LLOutfitsList::onCollapseAllFolders() -{ - for (outfits_map_t::iterator iter = mOutfitsMap.begin(); - iter != mOutfitsMap.end(); - ++iter) - { - LLAccordionCtrlTab* tab = iter->second; - if(tab && tab->isExpanded()) - { - tab->changeOpenClose(true); - } - } -} - -void LLOutfitsList::onExpandAllFolders() -{ - for (outfits_map_t::iterator iter = mOutfitsMap.begin(); - iter != mOutfitsMap.end(); - ++iter) - { - LLAccordionCtrlTab* tab = iter->second; - if(tab && !tab->isExpanded()) - { - tab->changeOpenClose(false); - } - } -} - -bool LLOutfitsList::hasItemSelected() -{ - return mItemSelected; -} - -////////////////////////////////////////////////////////////////////////// -// Private methods -////////////////////////////////////////////////////////////////////////// - -void LLOutfitsList::updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name) -{ - outfits_map_t::iterator outfits_iter = mOutfitsMap.find(cat->getUUID()); - if (outfits_iter != mOutfitsMap.end()) - { - // Update tab name with the new category name. - LLAccordionCtrlTab* tab = outfits_iter->second; - if (tab) - { - tab->setName(name); - tab->setTitle(name); - } - } -} - -void LLOutfitsList::resetItemSelection(LLWearableItemsList* list, const LLUUID& category_id) -{ - list->resetSelection(); - mItemSelected = false; - signalSelectionOutfitUUID(category_id); -} - -void LLOutfitsList::onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) -{ - MASK mask = gKeyboard->currentMask(true); - - // Reset selection in all previously selected tabs except for the current - // if new selection is started. - if (list && !(mask & MASK_CONTROL)) - { - for (wearables_lists_map_t::iterator iter = mSelectedListsMap.begin(); - iter != mSelectedListsMap.end(); - ++iter) - { - LLWearableItemsList* selected_list = (*iter).second; - if (selected_list != list) - { - selected_list->resetSelection(); - } - } - - // Clear current selection. - mSelectedListsMap.clear(); - } - - mItemSelected = list && (list->getSelectedItem() != NULL); - - mSelectedListsMap.insert(wearables_lists_map_value_t(category_id, list)); -} - -void LLOutfitsList::deselectOutfit(const LLUUID& category_id) -{ - // Remove selected lists map entry. - mSelectedListsMap.erase(category_id); - - LLOutfitListBase::deselectOutfit(category_id); -} - -void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID& category_id) -{ - // Try restoring outfit selection after filtering. - if (mAccordion->getSelectedTab() == tab) - { - signalSelectionOutfitUUID(category_id); - } -} - -void LLOutfitsList::onRefreshComplete(LLUICtrl* ctrl) -{ - if (!ctrl || getFilterSubString().empty()) - return; - - for (outfits_map_t::iterator - iter = mOutfitsMap.begin(), - iter_end = mOutfitsMap.end(); - iter != iter_end; ++iter) - { - LLAccordionCtrlTab* tab = iter->second; - if (!tab) continue; - - LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); - if (list != ctrl) continue; - - applyFilterToTab(iter->first, tab, getFilterSubString()); - } -} - -// virtual -void LLOutfitsList::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string) -{ - mAccordion->setFilterSubString(new_string); - - outfits_map_t::iterator iter = mOutfitsMap.begin(), iter_end = mOutfitsMap.end(); - while (iter != iter_end) - { - const LLUUID& category_id = iter->first; - LLAccordionCtrlTab* tab = iter++->second; - if (!tab) continue; - - LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); - if (list) - { - list->setFilterSubString(new_string, tab->getDisplayChildren()); - } - - if (old_string.empty()) - { - // Store accordion tab state when filter is not empty - tab->notifyChildren(LLSD().with("action", "store_state")); - } - - if (!new_string.empty()) - { - applyFilterToTab(category_id, tab, new_string); - } - else - { - tab->setVisible(true); - - // Restore tab title when filter is empty - tab->setTitle(tab->getTitle()); - - // Restore accordion state after all those accodrion tab manipulations - tab->notifyChildren(LLSD().with("action", "restore_state")); - - // Try restoring the tab selection. - restoreOutfitSelection(tab, category_id); - } - } - - mAccordion->arrange(); -} - -void LLOutfitsList::applyFilterToTab( - const LLUUID& category_id, - LLAccordionCtrlTab* tab, - const std::string& filter_substring) -{ - if (!tab) return; - LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); - if (!list) return; - - std::string title = tab->getTitle(); - LLStringUtil::toUpper(title); - - std::string cur_filter = filter_substring; - LLStringUtil::toUpper(cur_filter); - - tab->setTitle(tab->getTitle(), cur_filter); - - if (std::string::npos == title.find(cur_filter)) - { - // Hide tab if its title doesn't pass filter - // and it has no matched items - tab->setVisible(list->hasMatchedItems()); - - // Remove title highlighting because it might - // have been previously highlighted by less restrictive filter - tab->setTitle(tab->getTitle()); - - // Remove the tab from selection. - deselectOutfit(category_id); - } - else - { - // Try restoring the tab selection. - restoreOutfitSelection(tab, category_id); - } -} - -bool LLOutfitsList::canWearSelected() -{ - if (!isAgentAvatarValid()) - { - return false; - } - - uuid_vec_t selected_items; - getSelectedItemsUUIDs(selected_items); - S32 nonreplacable_objects = 0; - - for (uuid_vec_t::const_iterator it = selected_items.begin(); it != selected_items.end(); ++it) - { - const LLUUID& id = *it; - - // Check whether the item is worn. - if (!get_can_item_be_worn(id)) - { - return false; - } - - const LLViewerInventoryItem* item = gInventory.getItem(id); - if (!item) - { - return false; - } - - if (item->getType() == LLAssetType::AT_OBJECT) - { - nonreplacable_objects++; - } - } - - // All selected items can be worn. But do we have enough space for them? - return nonreplacable_objects == 0 || gAgentAvatarp->canAttachMoreObjects(nonreplacable_objects); -} - -void LLOutfitsList::wearSelectedItems() -{ - uuid_vec_t selected_uuids; - getSelectedItemsUUIDs(selected_uuids); - - if(selected_uuids.empty()) - { - return; - } - - wear_multiple(selected_uuids, false); -} - -void LLOutfitsList::onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y) -{ - LLWearableItemsList* list = dynamic_cast(ctrl); - if (!list) return; - - uuid_vec_t selected_uuids; - - getSelectedItemsUUIDs(selected_uuids); - - LLWearableItemsList::ContextMenu::instance().show(list, selected_uuids, x, y); -} - -void LLOutfitsList::onCOFChanged() -{ - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - - // Collect current COF items - gInventory.collectDescendents( - LLAppearanceMgr::instance().getCOF(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - - uuid_vec_t vnew; - uuid_vec_t vadded; - uuid_vec_t vremoved; - - // From gInventory we get the UUIDs of links that are currently in COF. - // These links UUIDs are not the same UUIDs that we have in each wearable items list. - // So we collect base items' UUIDs to find them or links that point to them in wearable - // items lists and update their worn state there. - LLInventoryModel::item_array_t::const_iterator array_iter = item_array.begin(), array_end = item_array.end(); - while (array_iter < array_end) - { - vnew.push_back((*(array_iter++))->getLinkedUUID()); - } - - // We need to update only items that were added or removed from COF. - LLCommonUtils::computeDifference(vnew, mCOFLinkedItems, vadded, vremoved); - - // Store the ids of items currently linked from COF. - mCOFLinkedItems = vnew; - - // Append removed ids to added ids because we should update all of them. - vadded.reserve(vadded.size() + vremoved.size()); - vadded.insert(vadded.end(), vremoved.begin(), vremoved.end()); - vremoved.clear(); - - outfits_map_t::iterator map_iter = mOutfitsMap.begin(), map_end = mOutfitsMap.end(); - while (map_iter != map_end) - { - LLAccordionCtrlTab* tab = (map_iter++)->second; - if (!tab) continue; - - LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); - if (!list) continue; - - // Every list updates the labels of changed items or - // the links that point to these items. - list->updateChangedItems(vadded); - } -} - -void LLOutfitsList::getCurrentCategories(uuid_vec_t& vcur) -{ - // Creating a vector of currently displayed sub-categories UUIDs. - for (outfits_map_t::const_iterator iter = mOutfitsMap.begin(); - iter != mOutfitsMap.end(); - iter++) - { - vcur.push_back((*iter).first); - } -} - - -void LLOutfitsList::sortOutfits() -{ - mAccordion->sort(); -} - -void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) -{ - LLAccordionCtrlTab* tab = dynamic_cast(ctrl); - if (mOutfitMenu && is_tab_header_clicked(tab, y) && cat_id.notNull()) - { - // Focus tab header to trigger tab selection change. - LLUICtrl* header = tab->findChild("dd_header"); - if (header) - { - header->setFocus(true); - } - - uuid_vec_t selected_uuids; - selected_uuids.push_back(cat_id); - mOutfitMenu->show(ctrl, selected_uuids, x, y); - } -} - -LLOutfitListGearMenuBase* LLOutfitsList::createGearMenu() -{ - return new LLOutfitListGearMenu(this); -} - - -bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y) -{ - if(!tab || !tab->getHeaderVisible()) return false; - - S32 header_bottom = tab->getLocalRect().getHeight() - tab->getHeaderHeight(); - return y >= header_bottom; -} - -LLOutfitListBase::LLOutfitListBase() - : LLPanelAppearanceTab() - , mIsInitialized(false) -{ - mCategoriesObserver = new LLInventoryCategoriesObserver(); - mOutfitMenu = new LLOutfitContextMenu(this); - //mGearMenu = createGearMenu(); -} - -LLOutfitListBase::~LLOutfitListBase() -{ - delete mOutfitMenu; - delete mGearMenu; - - if (gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - } - delete mCategoriesObserver; -} - -void LLOutfitListBase::onOpen(const LLSD& info) -{ - if (!mIsInitialized) - { - // *TODO: I'm not sure is this check necessary but it never match while developing. - if (!gInventory.isInventoryUsable()) - return; - - const LLUUID outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - - // *TODO: I'm not sure is this check necessary but it never match while developing. - LLViewerInventoryCategory* category = gInventory.getCategory(outfits); - if (!category) - return; - - gInventory.addObserver(mCategoriesObserver); - - // Start observing changes in "My Outfits" category. - mCategoriesObserver->addCategory(outfits, - boost::bind(&LLOutfitListBase::observerCallback, this, outfits)); - - //const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); - // Start observing changes in Current Outfit category. - //mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); - - LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(&LLOutfitListBase::highlightBaseOutfit, this)); - LLOutfitObserver::instance().addBOFReplacedCallback(boost::bind(&LLOutfitListBase::highlightBaseOutfit, this)); - - // Fetch "My Outfits" contents and refresh the list to display - // initially fetched items. If not all items are fetched now - // the observer will refresh the list as soon as the new items - // arrive. - category->fetch(); - refreshList(outfits); - - mIsInitialized = true; - } -} - -void LLOutfitListBase::observerCallback(const LLUUID& category_id) -{ - const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); - mChangedItems.insert(changed_items.begin(), changed_items.end()); - refreshList(category_id); -} - -void LLOutfitListBase::refreshList(const LLUUID& category_id) -{ - bool wasNull = mRefreshListState.CategoryUUID.isNull(); - mRefreshListState.CategoryUUID.setNull(); - - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - - // Collect all sub-categories of a given category. - LLIsType is_category(LLAssetType::AT_CATEGORY); - gInventory.collectDescendentsIf( - category_id, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_category); - - // Memorize item names for each UUID - std::map names; - for (const LLPointer& cat : cat_array) - { - names.emplace(std::make_pair(cat->getUUID(), cat->getName())); - } - - // Fill added and removed items vectors. - mRefreshListState.Added.clear(); - mRefreshListState.Removed.clear(); - computeDifference(cat_array, mRefreshListState.Added, mRefreshListState.Removed); - // Sort added items vector by item name. - std::sort(mRefreshListState.Added.begin(), mRefreshListState.Added.end(), - [names](const LLUUID& a, const LLUUID& b) - { - return LLStringUtil::compareDict(names.at(a), names.at(b)) < 0; - }); - // Initialize iterators for added and removed items vectors. - mRefreshListState.AddedIterator = mRefreshListState.Added.begin(); - mRefreshListState.RemovedIterator = mRefreshListState.Removed.begin(); - - LL_INFOS() << "added: " << mRefreshListState.Added.size() << - ", removed: " << mRefreshListState.Removed.size() << - ", changed: " << gInventory.getChangedIDs().size() << - LL_ENDL; - - mRefreshListState.CategoryUUID = category_id; - if (wasNull) - { - gIdleCallbacks.addFunction(onIdle, this); - } -} - -// static -void LLOutfitListBase::onIdle(void* userdata) -{ - LLOutfitListBase* self = (LLOutfitListBase*)userdata; - - self->onIdleRefreshList(); -} - -void LLOutfitListBase::onIdleRefreshList() -{ - if (mRefreshListState.CategoryUUID.isNull()) - return; - - const F64 MAX_TIME = 0.05f; - F64 curent_time = LLTimer::getTotalSeconds(); - const F64 end_time = curent_time + MAX_TIME; - - // Handle added tabs. - while (mRefreshListState.AddedIterator < mRefreshListState.Added.end()) - { - const LLUUID cat_id = (*mRefreshListState.AddedIterator++); - updateAddedCategory(cat_id); - - curent_time = LLTimer::getTotalSeconds(); - if (curent_time >= end_time) - return; - } - mRefreshListState.Added.clear(); - mRefreshListState.AddedIterator = mRefreshListState.Added.end(); - - // Handle removed tabs. - while (mRefreshListState.RemovedIterator < mRefreshListState.Removed.end()) - { - const LLUUID cat_id = (*mRefreshListState.RemovedIterator++); - updateRemovedCategory(cat_id); - - curent_time = LLTimer::getTotalSeconds(); - if (curent_time >= end_time) - return; - } - mRefreshListState.Removed.clear(); - mRefreshListState.RemovedIterator = mRefreshListState.Removed.end(); - - // Get changed items from inventory model and update outfit tabs - // which might have been renamed. - while (!mChangedItems.empty()) - { - std::set::const_iterator items_iter = mChangedItems.begin(); - LLViewerInventoryCategory *cat = gInventory.getCategory(*items_iter); - mChangedItems.erase(items_iter); - - // Links aren't supposed to be allowed here, check only cats - if (cat) - { - std::string name = cat->getName(); - updateChangedCategoryName(cat, name); - } - - curent_time = LLTimer::getTotalSeconds(); - if (curent_time >= end_time) - return; - } - - sortOutfits(); - highlightBaseOutfit(); - - gIdleCallbacks.deleteFunction(onIdle, this); - mRefreshListState.CategoryUUID.setNull(); - - LL_INFOS() << "done" << LL_ENDL; -} - -void LLOutfitListBase::computeDifference( - const LLInventoryModel::cat_array_t& vcats, - uuid_vec_t& vadded, - uuid_vec_t& vremoved) -{ - uuid_vec_t vnew; - // Creating a vector of newly collected sub-categories UUIDs. - for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin(); - iter != vcats.end(); - iter++) - { - vnew.push_back((*iter)->getUUID()); - } - - uuid_vec_t vcur; - getCurrentCategories(vcur); - - LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); -} - -void LLOutfitListBase::sortOutfits() -{ -} - -void LLOutfitListBase::highlightBaseOutfit() -{ - // id of base outfit - LLUUID base_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); - if (base_id != mHighlightedOutfitUUID) - { - LLUUID prev_id = mHighlightedOutfitUUID; - mHighlightedOutfitUUID = base_id; - onHighlightBaseOutfit(base_id, prev_id); - } -} - -void LLOutfitListBase::removeSelected() -{ - LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitListBase::onOutfitsRemovalConfirmation, this, _1, _2)); -} - -void LLOutfitListBase::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - - if (mSelectedOutfitUUID.notNull()) - { - gInventory.removeCategory(mSelectedOutfitUUID); - } -} - -void LLOutfitListBase::setSelectedOutfitByUUID(const LLUUID& outfit_uuid) -{ - onSetSelectedOutfitByUUID(outfit_uuid); -} - -boost::signals2::connection LLOutfitListBase::setSelectionChangeCallback(selection_change_callback_t cb) -{ - return mSelectionChangeSignal.connect(cb); -} - -void LLOutfitListBase::signalSelectionOutfitUUID(const LLUUID& category_id) -{ - mSelectionChangeSignal(category_id); -} - -void LLOutfitListBase::outfitRightClickCallBack(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) -{ - onOutfitRightClick(ctrl, x, y, cat_id); -} - -void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) -{ - onChangeOutfitSelection(list, category_id); - mSelectedOutfitUUID = category_id; - signalSelectionOutfitUUID(category_id); -} - -bool LLOutfitListBase::postBuild() -{ - mGearMenu = createGearMenu(); - - LLMenuButton* menu_gear_btn = getChild("options_gear_btn"); - - menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenuBase::updateItemsVisibility, mGearMenu)); - menu_gear_btn->setMenu(mGearMenu->getMenu()); - return true; -} - -void LLOutfitListBase::collapseAllFolders() -{ - onCollapseAllFolders(); -} - -void LLOutfitListBase::expandAllFolders() -{ - onExpandAllFolders(); -} - -void LLOutfitListBase::deselectOutfit(const LLUUID& category_id) -{ - // Reset selection if the outfit is selected. - if (category_id == mSelectedOutfitUUID) - { - mSelectedOutfitUUID = LLUUID::null; - signalSelectionOutfitUUID(mSelectedOutfitUUID); - } -} - -LLContextMenu* LLOutfitContextMenu::createMenu() -{ - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - LLUUID selected_id = mUUIDs.front(); - - registrar.add("Outfit.WearReplace", - boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); - registrar.add("Outfit.WearAdd", - boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); - registrar.add("Outfit.TakeOff", - boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); - registrar.add("Outfit.Edit", boost::bind(editOutfit)); - registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); - registrar.add("Outfit.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); - registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitContextMenu::onThumbnail, this, selected_id)); - registrar.add("Outfit.Save", boost::bind(&LLOutfitContextMenu::onSave, this, selected_id)); - - enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2)); - enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitContextMenu::onVisible, this, _2)); - - return createFromFile("menu_outfit_tab.xml"); - -} - -bool LLOutfitContextMenu::onEnable(LLSD::String param) -{ - LLUUID outfit_cat_id = mUUIDs.back(); - - if ("rename" == param) - { - return get_is_category_renameable(&gInventory, outfit_cat_id); - } - else if ("wear_replace" == param) - { - return LLAppearanceMgr::instance().getCanReplaceCOF(outfit_cat_id); - } - else if ("wear_add" == param) - { - return LLAppearanceMgr::getCanAddToCOF(outfit_cat_id); - } - else if ("take_off" == param) - { - return LLAppearanceMgr::getCanRemoveFromCOF(outfit_cat_id); - } - - return true; -} - -bool LLOutfitContextMenu::onVisible(LLSD::String param) -{ - LLUUID outfit_cat_id = mUUIDs.back(); - - if ("edit" == param) - { - bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id; - return is_worn; - } - else if ("wear_replace" == param) - { - return true; - } - else if ("delete" == param) - { - return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id); - } - - return true; -} - -//static -void LLOutfitContextMenu::editOutfit() -{ - LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit")); -} - -void LLOutfitContextMenu::renameOutfit(const LLUUID& outfit_cat_id) -{ - LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); -} - -void LLOutfitContextMenu::onThumbnail(const LLUUID &outfit_cat_id) -{ - if (outfit_cat_id.notNull()) - { - LLSD data(outfit_cat_id); - LLFloaterReg::showInstance("change_item_thumbnail", data); - } -} - -void LLOutfitContextMenu::onSave(const LLUUID &outfit_cat_id) -{ - if (outfit_cat_id.notNull()) - { - LLNotificationsUtil::add("ConfirmOverwriteOutfit", LLSD(), LLSD(), - [outfit_cat_id](const LLSD ¬if, const LLSD &resp) - { - S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); - if (opt == 0) - { - LLAppearanceMgr::getInstance()->onOutfitFolderCreated(outfit_cat_id, true); - } - }); - } -} - -LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist) - : mOutfitList(olist), - mMenu(NULL) -{ - llassert_always(mOutfitList); - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenuBase::onWear, this)); - registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenuBase::onTakeOff, this)); - registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenuBase::onRename, this)); - registrar.add("Gear.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); - registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenuBase::onCreate, this, _2)); - registrar.add("Gear.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, mOutfitList)); - registrar.add("Gear.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, mOutfitList)); - - registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this)); - registrar.add("Gear.Save", boost::bind(&LLOutfitListGearMenuBase::onSave, this)); - - registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this)); - registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this)); - - enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2)); - enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenuBase::onVisible, this, _2)); - - mMenu = LLUICtrlFactory::getInstance()->createFromFile( - "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - llassert(mMenu); -} - -LLOutfitListGearMenuBase::~LLOutfitListGearMenuBase() -{} - -void LLOutfitListGearMenuBase::updateItemsVisibility() -{ - onUpdateItemsVisibility(); -} - -void LLOutfitListGearMenuBase::onUpdateItemsVisibility() -{ - if (!mMenu) return; - - bool have_selection = getSelectedOutfitID().notNull(); - mMenu->setItemVisible("wear_separator", have_selection); - mMenu->arrangeAndClear(); // update menu height -} - -LLToggleableMenu* LLOutfitListGearMenuBase::getMenu() -{ - return mMenu; -} -const LLUUID& LLOutfitListGearMenuBase::getSelectedOutfitID() -{ - return mOutfitList->getSelectedOutfitUUID(); -} - -LLViewerInventoryCategory* LLOutfitListGearMenuBase::getSelectedOutfit() -{ - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.isNull()) - { - return NULL; - } - - LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); - return cat; -} - -void LLOutfitListGearMenuBase::onWear() -{ - LLViewerInventoryCategory* selected_outfit = getSelectedOutfit(); - if (selected_outfit) - { - LLAppearanceMgr::instance().wearInventoryCategory( - selected_outfit, /*copy=*/ false, /*append=*/ false); - } -} - -void LLOutfitListGearMenuBase::onAdd() -{ - const LLUUID& selected_id = getSelectedOutfitID(); - - if (selected_id.notNull()) - { - LLAppearanceMgr::getInstance()->addCategoryToCurrentOutfit(selected_id); - } -} - -void LLOutfitListGearMenuBase::onSave() -{ - const LLUUID &selected_id = getSelectedOutfitID(); - LLNotificationsUtil::add("ConfirmOverwriteOutfit", LLSD(), LLSD(), - [selected_id](const LLSD ¬if, const LLSD &resp) - { - S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); - if (opt == 0) - { - LLAppearanceMgr::getInstance()->onOutfitFolderCreated(selected_id, true); - } - }); -} - -void LLOutfitListGearMenuBase::onTakeOff() -{ - // Take off selected outfit. - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.notNull()) - { - LLAppearanceMgr::instance().takeOffOutfit(selected_outfit_id); - } -} - -void LLOutfitListGearMenuBase::onRename() -{ - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.notNull()) - { - LLAppearanceMgr::instance().renameOutfit(selected_outfit_id); - } -} - -void LLOutfitListGearMenuBase::onCreate(const LLSD& data) -{ - LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(data.asString()); - if (type == LLWearableType::WT_NONE) - { - LL_WARNS() << "Invalid wearable type" << LL_ENDL; - return; - } - - LLAgentWearables::createWearable(type, true); -} - -bool LLOutfitListGearMenuBase::onEnable(LLSD::String param) -{ - // Handle the "Wear - Replace Current Outfit" menu option specially - // because LLOutfitList::isActionEnabled() checks whether it's allowed - // to wear selected outfit OR selected items, while we're only - // interested in the outfit (STORM-183). - if ("wear" == param) - { - return LLAppearanceMgr::instance().getCanReplaceCOF(mOutfitList->getSelectedOutfitUUID()); - } - - return mOutfitList->isActionEnabled(param); -} - -bool LLOutfitListGearMenuBase::onVisible(LLSD::String param) -{ - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.isNull()) // no selection or invalid outfit selected - { - return false; - } - - return true; -} - -void LLOutfitListGearMenuBase::onThumbnail() -{ - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - LLSD data(selected_outfit_id); - LLFloaterReg::showInstance("change_item_thumbnail", data); -} - -void LLOutfitListGearMenuBase::onChangeSortOrder() -{ - -} - -LLOutfitListGearMenu::LLOutfitListGearMenu(LLOutfitListBase* olist) - : LLOutfitListGearMenuBase(olist) -{} - -LLOutfitListGearMenu::~LLOutfitListGearMenu() -{} - -void LLOutfitListGearMenu::onUpdateItemsVisibility() -{ - if (!mMenu) return; - mMenu->setItemVisible("expand", true); - mMenu->setItemVisible("collapse", true); - mMenu->setItemVisible("thumbnail", getSelectedOutfitID().notNull()); - mMenu->setItemVisible("sepatator3", false); - mMenu->setItemVisible("sort_folders_by_name", false); - LLOutfitListGearMenuBase::onUpdateItemsVisibility(); -} - -bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) -{ - if (y >= getLocalRect().getHeight() - getHeaderHeight()) - { - LLSD params; - params["inv_type"] = LLInventoryType::IT_CATEGORY; - params["thumbnail_id"] = gInventory.getCategory(mFolderID)->getThumbnailUUID(); - params["item_id"] = mFolderID; - - LLToolTipMgr::instance().show(LLToolTip::Params() - .message(getToolTip()) - .sticky_rect(calcScreenRect()) - .delay_time(LLView::getTooltipTimeout()) - .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) - .create_params(params)); - return true; - } - - return LLAccordionCtrlTab::handleToolTip(x, y, mask); -} -// EOF +/** + * @file lloutfitslist.cpp + * @brief List of agent's outfits for My Appearance side panel. + * + * $LicenseInfo:firstyear=2010&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 "lloutfitslist.h" + +// llcommon +#include "llcommonutils.h" + +#include "llaccordionctrl.h" +#include "llaccordionctrltab.h" +#include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llfloaterreg.h" +#include "llfloatersidepanelcontainer.h" +#include "llinspecttexture.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "llmenubutton.h" +#include "llnotificationsutil.h" +#include "lloutfitobserver.h" +#include "lltoggleablemenu.h" +#include "lltransutil.h" +#include "llviewermenu.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "llwearableitemslist.h" + +static bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y); + +static const LLOutfitTabNameComparator OUTFIT_TAB_NAME_COMPARATOR; + +/*virtual*/ +bool LLOutfitTabNameComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const +{ + std::string name1 = tab1->getTitle(); + std::string name2 = tab2->getTitle(); + + return (LLStringUtil::compareDict(name1, name2) < 0); +} + +struct outfit_accordion_tab_params : public LLInitParam::Block +{ + Mandatory wearable_list; + + outfit_accordion_tab_params() + : wearable_list("wearable_items_list") + {} +}; + +const outfit_accordion_tab_params& get_accordion_tab_params() +{ + static outfit_accordion_tab_params tab_params; + static bool initialized = false; + if (!initialized) + { + initialized = true; + + LLXMLNodePtr xmlNode; + if (LLUICtrlFactory::getLayeredXMLNode("outfit_accordion_tab.xml", xmlNode)) + { + LLXUIParser parser; + parser.readXUI(xmlNode, tab_params, "outfit_accordion_tab.xml"); + } + else + { + LL_WARNS() << "Failed to read xml of Outfit's Accordion Tab from outfit_accordion_tab.xml" << LL_ENDL; + } + } + + return tab_params; +} + + +static LLPanelInjector t_outfits_list("outfits_list"); + +LLOutfitsList::LLOutfitsList() + : LLOutfitListBase() + , mAccordion(NULL) + , mListCommands(NULL) + , mItemSelected(false) +{ +} + +LLOutfitsList::~LLOutfitsList() +{ +} + +bool LLOutfitsList::postBuild() +{ + mAccordion = getChild("outfits_accordion"); + mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); + + return LLOutfitListBase::postBuild(); +} + +//virtual +void LLOutfitsList::onOpen(const LLSD& info) +{ + if (!mIsInitialized) + { + // Start observing changes in Current Outfit category. + LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLOutfitsList::onCOFChanged, this)); + } + + LLOutfitListBase::onOpen(info); + + LLAccordionCtrlTab* selected_tab = mAccordion->getSelectedTab(); + if (!selected_tab) return; + + // Pass focus to the selected outfit tab. + selected_tab->showAndFocusHeader(); +} + + +void LLOutfitsList::updateAddedCategory(LLUUID cat_id) +{ + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + if (!cat) return; + + std::string name = cat->getName(); + + outfit_accordion_tab_params tab_params(get_accordion_tab_params()); + tab_params.cat_id = cat_id; + LLOutfitAccordionCtrlTab *tab = LLUICtrlFactory::create(tab_params); + if (!tab) return; + LLWearableItemsList* wearable_list = LLUICtrlFactory::create(tab_params.wearable_list); + wearable_list->setShape(tab->getLocalRect()); + tab->addChild(wearable_list); + + tab->setName(name); + tab->setTitle(name); + + // *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated. + tab->setDisplayChildren(false); + mAccordion->addCollapsibleCtrl(tab); + + // Start observing the new outfit category. + LLWearableItemsList* list = tab->getChild("wearable_items_list"); + if (!mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id))) + { + // Remove accordion tab if category could not be added to observer. + mAccordion->removeCollapsibleCtrl(tab); + + // kill removed tab + tab->die(); + return; + } + + // Map the new tab with outfit category UUID. + mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab)); + + tab->setRightMouseDownCallback(boost::bind(&LLOutfitListBase::outfitRightClickCallBack, this, + _1, _2, _3, cat_id)); + + // Setting tab focus callback to monitor currently selected outfit. + tab->setFocusReceivedCallback(boost::bind(&LLOutfitListBase::ChangeOutfitSelection, this, list, cat_id)); + + // Setting callback to reset items selection inside outfit on accordion collapsing and expanding (EXT-7875) + tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::resetItemSelection, this, list, cat_id)); + + // force showing list items that don't match current filter(EXT-7158) + list->setForceShowingUnmatchedItems(true); + + // Setting list commit callback to monitor currently selected wearable item. + list->setCommitCallback(boost::bind(&LLOutfitsList::onListSelectionChange, this, _1)); + + // Setting list refresh callback to apply filter on list change. + list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onRefreshComplete, this, _1)); + + list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3)); + + // Fetch the new outfit contents. + cat->fetch(); + + // Refresh the list of outfit items after fetch(). + // Further list updates will be triggered by the category observer. + list->updateList(cat_id); + + // If filter is currently applied we store the initial tab state. + if (!getFilterSubString().empty()) + { + tab->notifyChildren(LLSD().with("action", "store_state")); + + // Setting mForceRefresh flag will make the list refresh its contents + // even if it is not currently visible. This is required to apply the + // filter to the newly added list. + list->setForceRefresh(true); + + list->setFilterSubString(getFilterSubString(), false); + } +} + +void LLOutfitsList::updateRemovedCategory(LLUUID cat_id) +{ + outfits_map_t::iterator outfits_iter = mOutfitsMap.find(cat_id); + if (outfits_iter != mOutfitsMap.end()) + { + const LLUUID& outfit_id = outfits_iter->first; + LLAccordionCtrlTab* tab = outfits_iter->second; + + // An outfit is removed from the list. Do the following: + // 1. Remove outfit category from observer to stop monitoring its changes. + mCategoriesObserver->removeCategory(outfit_id); + + // 2. Remove the outfit from selection. + deselectOutfit(outfit_id); + + // 3. Remove category UUID to accordion tab mapping. + mOutfitsMap.erase(outfits_iter); + + // 4. Remove outfit tab from accordion. + mAccordion->removeCollapsibleCtrl(tab); + + // kill removed tab + if (tab != NULL) + { + tab->die(); + } + } +} + +//virtual +void LLOutfitsList::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id) +{ + if (mOutfitsMap[prev_id]) + { + mOutfitsMap[prev_id]->setTitleFontStyle("NORMAL"); + mOutfitsMap[prev_id]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor")); + } + if (mOutfitsMap[base_id]) + { + mOutfitsMap[base_id]->setTitleFontStyle("BOLD"); + mOutfitsMap[base_id]->setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor")); + } +} + +void LLOutfitsList::onListSelectionChange(LLUICtrl* ctrl) +{ + LLWearableItemsList* list = dynamic_cast(ctrl); + if (!list) return; + + LLViewerInventoryItem *item = gInventory.getItem(list->getSelectedUUID()); + if (!item) return; + + ChangeOutfitSelection(list, item->getParentUUID()); +} + +void LLOutfitListBase::performAction(std::string action) +{ + if (mSelectedOutfitUUID.isNull()) return; + + LLViewerInventoryCategory* cat = gInventory.getCategory(mSelectedOutfitUUID); + if (!cat) return; + + if ("replaceoutfit" == action) + { + LLAppearanceMgr::instance().wearInventoryCategory( cat, false, false ); + } + else if ("addtooutfit" == action) + { + LLAppearanceMgr::instance().wearInventoryCategory( cat, false, true ); + } + else if ("rename_outfit" == action) + { + LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID); + } +} + +void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) +{ + for (outfits_map_t::iterator iter = mOutfitsMap.begin(); + iter != mOutfitsMap.end(); + ++iter) + { + if (outfit_uuid == iter->first) + { + LLAccordionCtrlTab* tab = iter->second; + if (!tab) continue; + + LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); + if (!list) continue; + + tab->setFocus(true); + ChangeOutfitSelection(list, outfit_uuid); + + tab->changeOpenClose(false); + } + } +} + +// virtual +bool LLOutfitListBase::isActionEnabled(const LLSD& userdata) +{ + if (mSelectedOutfitUUID.isNull()) return false; + + const std::string command_name = userdata.asString(); + if (command_name == "delete") + { + return !hasItemSelected() && LLAppearanceMgr::instance().getCanRemoveOutfit(mSelectedOutfitUUID); + } + if (command_name == "rename") + { + return get_is_category_renameable(&gInventory, mSelectedOutfitUUID); + } + if (command_name == "save_outfit") + { + bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); + bool outfit_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); + // allow save only if outfit isn't locked and is dirty + return !outfit_locked && outfit_dirty; + } + if (command_name == "wear") + { + if (gAgentWearables.isCOFChangeInProgress()) + { + return false; + } + + if (hasItemSelected()) + { + return canWearSelected(); + } + + // outfit selected + return LLAppearanceMgr::instance().getCanReplaceCOF(mSelectedOutfitUUID); + } + if (command_name == "take_off") + { + // Enable "Take Off" if any of selected items can be taken off + // or the selected outfit contains items that can be taken off. + return ( hasItemSelected() && canTakeOffSelected() ) + || ( !hasItemSelected() && LLAppearanceMgr::getCanRemoveFromCOF(mSelectedOutfitUUID) ); + } + + if (command_name == "wear_add") + { + // *TODO: do we ever get here? + return LLAppearanceMgr::getCanAddToCOF(mSelectedOutfitUUID); + } + + return false; +} + +void LLOutfitsList::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const +{ + // Collect selected items from all selected lists. + for (wearables_lists_map_t::const_iterator iter = mSelectedListsMap.begin(); + iter != mSelectedListsMap.end(); + ++iter) + { + uuid_vec_t uuids; + (*iter).second->getSelectedUUIDs(uuids); + + S32 prev_size = selected_uuids.size(); + selected_uuids.resize(prev_size + uuids.size()); + std::copy(uuids.begin(), uuids.end(), selected_uuids.begin() + prev_size); + } +} + +void LLOutfitsList::onCollapseAllFolders() +{ + for (outfits_map_t::iterator iter = mOutfitsMap.begin(); + iter != mOutfitsMap.end(); + ++iter) + { + LLAccordionCtrlTab* tab = iter->second; + if(tab && tab->isExpanded()) + { + tab->changeOpenClose(true); + } + } +} + +void LLOutfitsList::onExpandAllFolders() +{ + for (outfits_map_t::iterator iter = mOutfitsMap.begin(); + iter != mOutfitsMap.end(); + ++iter) + { + LLAccordionCtrlTab* tab = iter->second; + if(tab && !tab->isExpanded()) + { + tab->changeOpenClose(false); + } + } +} + +bool LLOutfitsList::hasItemSelected() +{ + return mItemSelected; +} + +////////////////////////////////////////////////////////////////////////// +// Private methods +////////////////////////////////////////////////////////////////////////// + +void LLOutfitsList::updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name) +{ + outfits_map_t::iterator outfits_iter = mOutfitsMap.find(cat->getUUID()); + if (outfits_iter != mOutfitsMap.end()) + { + // Update tab name with the new category name. + LLAccordionCtrlTab* tab = outfits_iter->second; + if (tab) + { + tab->setName(name); + tab->setTitle(name); + } + } +} + +void LLOutfitsList::resetItemSelection(LLWearableItemsList* list, const LLUUID& category_id) +{ + list->resetSelection(); + mItemSelected = false; + signalSelectionOutfitUUID(category_id); +} + +void LLOutfitsList::onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) +{ + MASK mask = gKeyboard->currentMask(true); + + // Reset selection in all previously selected tabs except for the current + // if new selection is started. + if (list && !(mask & MASK_CONTROL)) + { + for (wearables_lists_map_t::iterator iter = mSelectedListsMap.begin(); + iter != mSelectedListsMap.end(); + ++iter) + { + LLWearableItemsList* selected_list = (*iter).second; + if (selected_list != list) + { + selected_list->resetSelection(); + } + } + + // Clear current selection. + mSelectedListsMap.clear(); + } + + mItemSelected = list && (list->getSelectedItem() != NULL); + + mSelectedListsMap.insert(wearables_lists_map_value_t(category_id, list)); +} + +void LLOutfitsList::deselectOutfit(const LLUUID& category_id) +{ + // Remove selected lists map entry. + mSelectedListsMap.erase(category_id); + + LLOutfitListBase::deselectOutfit(category_id); +} + +void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID& category_id) +{ + // Try restoring outfit selection after filtering. + if (mAccordion->getSelectedTab() == tab) + { + signalSelectionOutfitUUID(category_id); + } +} + +void LLOutfitsList::onRefreshComplete(LLUICtrl* ctrl) +{ + if (!ctrl || getFilterSubString().empty()) + return; + + for (outfits_map_t::iterator + iter = mOutfitsMap.begin(), + iter_end = mOutfitsMap.end(); + iter != iter_end; ++iter) + { + LLAccordionCtrlTab* tab = iter->second; + if (!tab) continue; + + LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); + if (list != ctrl) continue; + + applyFilterToTab(iter->first, tab, getFilterSubString()); + } +} + +// virtual +void LLOutfitsList::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string) +{ + mAccordion->setFilterSubString(new_string); + + outfits_map_t::iterator iter = mOutfitsMap.begin(), iter_end = mOutfitsMap.end(); + while (iter != iter_end) + { + const LLUUID& category_id = iter->first; + LLAccordionCtrlTab* tab = iter++->second; + if (!tab) continue; + + LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); + if (list) + { + list->setFilterSubString(new_string, tab->getDisplayChildren()); + } + + if (old_string.empty()) + { + // Store accordion tab state when filter is not empty + tab->notifyChildren(LLSD().with("action", "store_state")); + } + + if (!new_string.empty()) + { + applyFilterToTab(category_id, tab, new_string); + } + else + { + tab->setVisible(true); + + // Restore tab title when filter is empty + tab->setTitle(tab->getTitle()); + + // Restore accordion state after all those accodrion tab manipulations + tab->notifyChildren(LLSD().with("action", "restore_state")); + + // Try restoring the tab selection. + restoreOutfitSelection(tab, category_id); + } + } + + mAccordion->arrange(); +} + +void LLOutfitsList::applyFilterToTab( + const LLUUID& category_id, + LLAccordionCtrlTab* tab, + const std::string& filter_substring) +{ + if (!tab) return; + LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); + if (!list) return; + + std::string title = tab->getTitle(); + LLStringUtil::toUpper(title); + + std::string cur_filter = filter_substring; + LLStringUtil::toUpper(cur_filter); + + tab->setTitle(tab->getTitle(), cur_filter); + + if (std::string::npos == title.find(cur_filter)) + { + // Hide tab if its title doesn't pass filter + // and it has no matched items + tab->setVisible(list->hasMatchedItems()); + + // Remove title highlighting because it might + // have been previously highlighted by less restrictive filter + tab->setTitle(tab->getTitle()); + + // Remove the tab from selection. + deselectOutfit(category_id); + } + else + { + // Try restoring the tab selection. + restoreOutfitSelection(tab, category_id); + } +} + +bool LLOutfitsList::canWearSelected() +{ + if (!isAgentAvatarValid()) + { + return false; + } + + uuid_vec_t selected_items; + getSelectedItemsUUIDs(selected_items); + S32 nonreplacable_objects = 0; + + for (uuid_vec_t::const_iterator it = selected_items.begin(); it != selected_items.end(); ++it) + { + const LLUUID& id = *it; + + // Check whether the item is worn. + if (!get_can_item_be_worn(id)) + { + return false; + } + + const LLViewerInventoryItem* item = gInventory.getItem(id); + if (!item) + { + return false; + } + + if (item->getType() == LLAssetType::AT_OBJECT) + { + nonreplacable_objects++; + } + } + + // All selected items can be worn. But do we have enough space for them? + return nonreplacable_objects == 0 || gAgentAvatarp->canAttachMoreObjects(nonreplacable_objects); +} + +void LLOutfitsList::wearSelectedItems() +{ + uuid_vec_t selected_uuids; + getSelectedItemsUUIDs(selected_uuids); + + if(selected_uuids.empty()) + { + return; + } + + wear_multiple(selected_uuids, false); +} + +void LLOutfitsList::onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y) +{ + LLWearableItemsList* list = dynamic_cast(ctrl); + if (!list) return; + + uuid_vec_t selected_uuids; + + getSelectedItemsUUIDs(selected_uuids); + + LLWearableItemsList::ContextMenu::instance().show(list, selected_uuids, x, y); +} + +void LLOutfitsList::onCOFChanged() +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + // Collect current COF items + gInventory.collectDescendents( + LLAppearanceMgr::instance().getCOF(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + + uuid_vec_t vnew; + uuid_vec_t vadded; + uuid_vec_t vremoved; + + // From gInventory we get the UUIDs of links that are currently in COF. + // These links UUIDs are not the same UUIDs that we have in each wearable items list. + // So we collect base items' UUIDs to find them or links that point to them in wearable + // items lists and update their worn state there. + LLInventoryModel::item_array_t::const_iterator array_iter = item_array.begin(), array_end = item_array.end(); + while (array_iter < array_end) + { + vnew.push_back((*(array_iter++))->getLinkedUUID()); + } + + // We need to update only items that were added or removed from COF. + LLCommonUtils::computeDifference(vnew, mCOFLinkedItems, vadded, vremoved); + + // Store the ids of items currently linked from COF. + mCOFLinkedItems = vnew; + + // Append removed ids to added ids because we should update all of them. + vadded.reserve(vadded.size() + vremoved.size()); + vadded.insert(vadded.end(), vremoved.begin(), vremoved.end()); + vremoved.clear(); + + outfits_map_t::iterator map_iter = mOutfitsMap.begin(), map_end = mOutfitsMap.end(); + while (map_iter != map_end) + { + LLAccordionCtrlTab* tab = (map_iter++)->second; + if (!tab) continue; + + LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); + if (!list) continue; + + // Every list updates the labels of changed items or + // the links that point to these items. + list->updateChangedItems(vadded); + } +} + +void LLOutfitsList::getCurrentCategories(uuid_vec_t& vcur) +{ + // Creating a vector of currently displayed sub-categories UUIDs. + for (outfits_map_t::const_iterator iter = mOutfitsMap.begin(); + iter != mOutfitsMap.end(); + iter++) + { + vcur.push_back((*iter).first); + } +} + + +void LLOutfitsList::sortOutfits() +{ + mAccordion->sort(); +} + +void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) +{ + LLAccordionCtrlTab* tab = dynamic_cast(ctrl); + if (mOutfitMenu && is_tab_header_clicked(tab, y) && cat_id.notNull()) + { + // Focus tab header to trigger tab selection change. + LLUICtrl* header = tab->findChild("dd_header"); + if (header) + { + header->setFocus(true); + } + + uuid_vec_t selected_uuids; + selected_uuids.push_back(cat_id); + mOutfitMenu->show(ctrl, selected_uuids, x, y); + } +} + +LLOutfitListGearMenuBase* LLOutfitsList::createGearMenu() +{ + return new LLOutfitListGearMenu(this); +} + + +bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y) +{ + if(!tab || !tab->getHeaderVisible()) return false; + + S32 header_bottom = tab->getLocalRect().getHeight() - tab->getHeaderHeight(); + return y >= header_bottom; +} + +LLOutfitListBase::LLOutfitListBase() + : LLPanelAppearanceTab() + , mIsInitialized(false) +{ + mCategoriesObserver = new LLInventoryCategoriesObserver(); + mOutfitMenu = new LLOutfitContextMenu(this); + //mGearMenu = createGearMenu(); +} + +LLOutfitListBase::~LLOutfitListBase() +{ + delete mOutfitMenu; + delete mGearMenu; + + if (gInventory.containsObserver(mCategoriesObserver)) + { + gInventory.removeObserver(mCategoriesObserver); + } + delete mCategoriesObserver; +} + +void LLOutfitListBase::onOpen(const LLSD& info) +{ + if (!mIsInitialized) + { + // *TODO: I'm not sure is this check necessary but it never match while developing. + if (!gInventory.isInventoryUsable()) + return; + + const LLUUID outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + // *TODO: I'm not sure is this check necessary but it never match while developing. + LLViewerInventoryCategory* category = gInventory.getCategory(outfits); + if (!category) + return; + + gInventory.addObserver(mCategoriesObserver); + + // Start observing changes in "My Outfits" category. + mCategoriesObserver->addCategory(outfits, + boost::bind(&LLOutfitListBase::observerCallback, this, outfits)); + + //const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + // Start observing changes in Current Outfit category. + //mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); + + LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(&LLOutfitListBase::highlightBaseOutfit, this)); + LLOutfitObserver::instance().addBOFReplacedCallback(boost::bind(&LLOutfitListBase::highlightBaseOutfit, this)); + + // Fetch "My Outfits" contents and refresh the list to display + // initially fetched items. If not all items are fetched now + // the observer will refresh the list as soon as the new items + // arrive. + category->fetch(); + refreshList(outfits); + + mIsInitialized = true; + } +} + +void LLOutfitListBase::observerCallback(const LLUUID& category_id) +{ + const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); + mChangedItems.insert(changed_items.begin(), changed_items.end()); + refreshList(category_id); +} + +void LLOutfitListBase::refreshList(const LLUUID& category_id) +{ + bool wasNull = mRefreshListState.CategoryUUID.isNull(); + mRefreshListState.CategoryUUID.setNull(); + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + // Collect all sub-categories of a given category. + LLIsType is_category(LLAssetType::AT_CATEGORY); + gInventory.collectDescendentsIf( + category_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_category); + + // Memorize item names for each UUID + std::map names; + for (const LLPointer& cat : cat_array) + { + names.emplace(std::make_pair(cat->getUUID(), cat->getName())); + } + + // Fill added and removed items vectors. + mRefreshListState.Added.clear(); + mRefreshListState.Removed.clear(); + computeDifference(cat_array, mRefreshListState.Added, mRefreshListState.Removed); + // Sort added items vector by item name. + std::sort(mRefreshListState.Added.begin(), mRefreshListState.Added.end(), + [names](const LLUUID& a, const LLUUID& b) + { + return LLStringUtil::compareDict(names.at(a), names.at(b)) < 0; + }); + // Initialize iterators for added and removed items vectors. + mRefreshListState.AddedIterator = mRefreshListState.Added.begin(); + mRefreshListState.RemovedIterator = mRefreshListState.Removed.begin(); + + LL_INFOS() << "added: " << mRefreshListState.Added.size() << + ", removed: " << mRefreshListState.Removed.size() << + ", changed: " << gInventory.getChangedIDs().size() << + LL_ENDL; + + mRefreshListState.CategoryUUID = category_id; + if (wasNull) + { + gIdleCallbacks.addFunction(onIdle, this); + } +} + +// static +void LLOutfitListBase::onIdle(void* userdata) +{ + LLOutfitListBase* self = (LLOutfitListBase*)userdata; + + self->onIdleRefreshList(); +} + +void LLOutfitListBase::onIdleRefreshList() +{ + if (mRefreshListState.CategoryUUID.isNull()) + return; + + const F64 MAX_TIME = 0.05f; + F64 curent_time = LLTimer::getTotalSeconds(); + const F64 end_time = curent_time + MAX_TIME; + + // Handle added tabs. + while (mRefreshListState.AddedIterator < mRefreshListState.Added.end()) + { + const LLUUID cat_id = (*mRefreshListState.AddedIterator++); + updateAddedCategory(cat_id); + + curent_time = LLTimer::getTotalSeconds(); + if (curent_time >= end_time) + return; + } + mRefreshListState.Added.clear(); + mRefreshListState.AddedIterator = mRefreshListState.Added.end(); + + // Handle removed tabs. + while (mRefreshListState.RemovedIterator < mRefreshListState.Removed.end()) + { + const LLUUID cat_id = (*mRefreshListState.RemovedIterator++); + updateRemovedCategory(cat_id); + + curent_time = LLTimer::getTotalSeconds(); + if (curent_time >= end_time) + return; + } + mRefreshListState.Removed.clear(); + mRefreshListState.RemovedIterator = mRefreshListState.Removed.end(); + + // Get changed items from inventory model and update outfit tabs + // which might have been renamed. + while (!mChangedItems.empty()) + { + std::set::const_iterator items_iter = mChangedItems.begin(); + LLViewerInventoryCategory *cat = gInventory.getCategory(*items_iter); + mChangedItems.erase(items_iter); + + // Links aren't supposed to be allowed here, check only cats + if (cat) + { + std::string name = cat->getName(); + updateChangedCategoryName(cat, name); + } + + curent_time = LLTimer::getTotalSeconds(); + if (curent_time >= end_time) + return; + } + + sortOutfits(); + highlightBaseOutfit(); + + gIdleCallbacks.deleteFunction(onIdle, this); + mRefreshListState.CategoryUUID.setNull(); + + LL_INFOS() << "done" << LL_ENDL; +} + +void LLOutfitListBase::computeDifference( + const LLInventoryModel::cat_array_t& vcats, + uuid_vec_t& vadded, + uuid_vec_t& vremoved) +{ + uuid_vec_t vnew; + // Creating a vector of newly collected sub-categories UUIDs. + for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin(); + iter != vcats.end(); + iter++) + { + vnew.push_back((*iter)->getUUID()); + } + + uuid_vec_t vcur; + getCurrentCategories(vcur); + + LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); +} + +void LLOutfitListBase::sortOutfits() +{ +} + +void LLOutfitListBase::highlightBaseOutfit() +{ + // id of base outfit + LLUUID base_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); + if (base_id != mHighlightedOutfitUUID) + { + LLUUID prev_id = mHighlightedOutfitUUID; + mHighlightedOutfitUUID = base_id; + onHighlightBaseOutfit(base_id, prev_id); + } +} + +void LLOutfitListBase::removeSelected() +{ + LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitListBase::onOutfitsRemovalConfirmation, this, _1, _2)); +} + +void LLOutfitListBase::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + if (mSelectedOutfitUUID.notNull()) + { + gInventory.removeCategory(mSelectedOutfitUUID); + } +} + +void LLOutfitListBase::setSelectedOutfitByUUID(const LLUUID& outfit_uuid) +{ + onSetSelectedOutfitByUUID(outfit_uuid); +} + +boost::signals2::connection LLOutfitListBase::setSelectionChangeCallback(selection_change_callback_t cb) +{ + return mSelectionChangeSignal.connect(cb); +} + +void LLOutfitListBase::signalSelectionOutfitUUID(const LLUUID& category_id) +{ + mSelectionChangeSignal(category_id); +} + +void LLOutfitListBase::outfitRightClickCallBack(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) +{ + onOutfitRightClick(ctrl, x, y, cat_id); +} + +void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) +{ + onChangeOutfitSelection(list, category_id); + mSelectedOutfitUUID = category_id; + signalSelectionOutfitUUID(category_id); +} + +bool LLOutfitListBase::postBuild() +{ + mGearMenu = createGearMenu(); + + LLMenuButton* menu_gear_btn = getChild("options_gear_btn"); + + menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenuBase::updateItemsVisibility, mGearMenu)); + menu_gear_btn->setMenu(mGearMenu->getMenu()); + return true; +} + +void LLOutfitListBase::collapseAllFolders() +{ + onCollapseAllFolders(); +} + +void LLOutfitListBase::expandAllFolders() +{ + onExpandAllFolders(); +} + +void LLOutfitListBase::deselectOutfit(const LLUUID& category_id) +{ + // Reset selection if the outfit is selected. + if (category_id == mSelectedOutfitUUID) + { + mSelectedOutfitUUID = LLUUID::null; + signalSelectionOutfitUUID(mSelectedOutfitUUID); + } +} + +LLContextMenu* LLOutfitContextMenu::createMenu() +{ + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.front(); + + registrar.add("Outfit.WearReplace", + boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.WearAdd", + boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.TakeOff", + boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.Edit", boost::bind(editOutfit)); + registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); + registrar.add("Outfit.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); + registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitContextMenu::onThumbnail, this, selected_id)); + registrar.add("Outfit.Save", boost::bind(&LLOutfitContextMenu::onSave, this, selected_id)); + + enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2)); + enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitContextMenu::onVisible, this, _2)); + + return createFromFile("menu_outfit_tab.xml"); + +} + +bool LLOutfitContextMenu::onEnable(LLSD::String param) +{ + LLUUID outfit_cat_id = mUUIDs.back(); + + if ("rename" == param) + { + return get_is_category_renameable(&gInventory, outfit_cat_id); + } + else if ("wear_replace" == param) + { + return LLAppearanceMgr::instance().getCanReplaceCOF(outfit_cat_id); + } + else if ("wear_add" == param) + { + return LLAppearanceMgr::getCanAddToCOF(outfit_cat_id); + } + else if ("take_off" == param) + { + return LLAppearanceMgr::getCanRemoveFromCOF(outfit_cat_id); + } + + return true; +} + +bool LLOutfitContextMenu::onVisible(LLSD::String param) +{ + LLUUID outfit_cat_id = mUUIDs.back(); + + if ("edit" == param) + { + bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id; + return is_worn; + } + else if ("wear_replace" == param) + { + return true; + } + else if ("delete" == param) + { + return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id); + } + + return true; +} + +//static +void LLOutfitContextMenu::editOutfit() +{ + LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit")); +} + +void LLOutfitContextMenu::renameOutfit(const LLUUID& outfit_cat_id) +{ + LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); +} + +void LLOutfitContextMenu::onThumbnail(const LLUUID &outfit_cat_id) +{ + if (outfit_cat_id.notNull()) + { + LLSD data(outfit_cat_id); + LLFloaterReg::showInstance("change_item_thumbnail", data); + } +} + +void LLOutfitContextMenu::onSave(const LLUUID &outfit_cat_id) +{ + if (outfit_cat_id.notNull()) + { + LLNotificationsUtil::add("ConfirmOverwriteOutfit", LLSD(), LLSD(), + [outfit_cat_id](const LLSD ¬if, const LLSD &resp) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); + if (opt == 0) + { + LLAppearanceMgr::getInstance()->onOutfitFolderCreated(outfit_cat_id, true); + } + }); + } +} + +LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist) + : mOutfitList(olist), + mMenu(NULL) +{ + llassert_always(mOutfitList); + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenuBase::onWear, this)); + registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenuBase::onTakeOff, this)); + registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenuBase::onRename, this)); + registrar.add("Gear.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); + registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenuBase::onCreate, this, _2)); + registrar.add("Gear.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, mOutfitList)); + registrar.add("Gear.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, mOutfitList)); + + registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this)); + registrar.add("Gear.Save", boost::bind(&LLOutfitListGearMenuBase::onSave, this)); + + registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this)); + registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this)); + + enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2)); + enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenuBase::onVisible, this, _2)); + + mMenu = LLUICtrlFactory::getInstance()->createFromFile( + "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + llassert(mMenu); +} + +LLOutfitListGearMenuBase::~LLOutfitListGearMenuBase() +{} + +void LLOutfitListGearMenuBase::updateItemsVisibility() +{ + onUpdateItemsVisibility(); +} + +void LLOutfitListGearMenuBase::onUpdateItemsVisibility() +{ + if (!mMenu) return; + + bool have_selection = getSelectedOutfitID().notNull(); + mMenu->setItemVisible("wear_separator", have_selection); + mMenu->arrangeAndClear(); // update menu height +} + +LLToggleableMenu* LLOutfitListGearMenuBase::getMenu() +{ + return mMenu; +} +const LLUUID& LLOutfitListGearMenuBase::getSelectedOutfitID() +{ + return mOutfitList->getSelectedOutfitUUID(); +} + +LLViewerInventoryCategory* LLOutfitListGearMenuBase::getSelectedOutfit() +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.isNull()) + { + return NULL; + } + + LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); + return cat; +} + +void LLOutfitListGearMenuBase::onWear() +{ + LLViewerInventoryCategory* selected_outfit = getSelectedOutfit(); + if (selected_outfit) + { + LLAppearanceMgr::instance().wearInventoryCategory( + selected_outfit, /*copy=*/ false, /*append=*/ false); + } +} + +void LLOutfitListGearMenuBase::onAdd() +{ + const LLUUID& selected_id = getSelectedOutfitID(); + + if (selected_id.notNull()) + { + LLAppearanceMgr::getInstance()->addCategoryToCurrentOutfit(selected_id); + } +} + +void LLOutfitListGearMenuBase::onSave() +{ + const LLUUID &selected_id = getSelectedOutfitID(); + LLNotificationsUtil::add("ConfirmOverwriteOutfit", LLSD(), LLSD(), + [selected_id](const LLSD ¬if, const LLSD &resp) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); + if (opt == 0) + { + LLAppearanceMgr::getInstance()->onOutfitFolderCreated(selected_id, true); + } + }); +} + +void LLOutfitListGearMenuBase::onTakeOff() +{ + // Take off selected outfit. + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.notNull()) + { + LLAppearanceMgr::instance().takeOffOutfit(selected_outfit_id); + } +} + +void LLOutfitListGearMenuBase::onRename() +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.notNull()) + { + LLAppearanceMgr::instance().renameOutfit(selected_outfit_id); + } +} + +void LLOutfitListGearMenuBase::onCreate(const LLSD& data) +{ + LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(data.asString()); + if (type == LLWearableType::WT_NONE) + { + LL_WARNS() << "Invalid wearable type" << LL_ENDL; + return; + } + + LLAgentWearables::createWearable(type, true); +} + +bool LLOutfitListGearMenuBase::onEnable(LLSD::String param) +{ + // Handle the "Wear - Replace Current Outfit" menu option specially + // because LLOutfitList::isActionEnabled() checks whether it's allowed + // to wear selected outfit OR selected items, while we're only + // interested in the outfit (STORM-183). + if ("wear" == param) + { + return LLAppearanceMgr::instance().getCanReplaceCOF(mOutfitList->getSelectedOutfitUUID()); + } + + return mOutfitList->isActionEnabled(param); +} + +bool LLOutfitListGearMenuBase::onVisible(LLSD::String param) +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.isNull()) // no selection or invalid outfit selected + { + return false; + } + + return true; +} + +void LLOutfitListGearMenuBase::onThumbnail() +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + LLSD data(selected_outfit_id); + LLFloaterReg::showInstance("change_item_thumbnail", data); +} + +void LLOutfitListGearMenuBase::onChangeSortOrder() +{ + +} + +LLOutfitListGearMenu::LLOutfitListGearMenu(LLOutfitListBase* olist) + : LLOutfitListGearMenuBase(olist) +{} + +LLOutfitListGearMenu::~LLOutfitListGearMenu() +{} + +void LLOutfitListGearMenu::onUpdateItemsVisibility() +{ + if (!mMenu) return; + mMenu->setItemVisible("expand", true); + mMenu->setItemVisible("collapse", true); + mMenu->setItemVisible("thumbnail", getSelectedOutfitID().notNull()); + mMenu->setItemVisible("sepatator3", false); + mMenu->setItemVisible("sort_folders_by_name", false); + LLOutfitListGearMenuBase::onUpdateItemsVisibility(); +} + +bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) +{ + if (y >= getLocalRect().getHeight() - getHeaderHeight()) + { + LLSD params; + params["inv_type"] = LLInventoryType::IT_CATEGORY; + params["thumbnail_id"] = gInventory.getCategory(mFolderID)->getThumbnailUUID(); + params["item_id"] = mFolderID; + + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(getToolTip()) + .sticky_rect(calcScreenRect()) + .delay_time(LLView::getTooltipTimeout()) + .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) + .create_params(params)); + return true; + } + + return LLAccordionCtrlTab::handleToolTip(x, y, mask); +} +// EOF -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/newview/lloutfitslist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/lloutfitslist.cpp') diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 94b32ceea9..0f5f7aebf8 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -376,7 +376,7 @@ void LLOutfitsList::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const uuid_vec_t uuids; (*iter).second->getSelectedUUIDs(uuids); - S32 prev_size = selected_uuids.size(); + auto prev_size = selected_uuids.size(); selected_uuids.resize(prev_size + uuids.size()); std::copy(uuids.begin(), uuids.end(), selected_uuids.begin() + prev_size); } -- cgit v1.2.3