/indra/newview/lloutfitslist.cpp
https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 1113 lines · 784 code · 202 blank · 127 comment · 160 complexity · eb0c174e6a326b5f1183900deec50afb MD5 · raw file
- /**
- * @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 "llfloatersidepanelcontainer.h"
- #include "llinventoryfunctions.h"
- #include "llinventorymodel.h"
- #include "lllistcontextmenu.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();
- LLStringUtil::toUpper(name1);
- LLStringUtil::toUpper(name2);
- return name1 < name2;
- }
- struct outfit_accordion_tab_params : public LLInitParam::Block<outfit_accordion_tab_params, LLAccordionCtrlTab::Params>
- {
- Mandatory<LLWearableItemsList::Params> 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
- {
- llwarns << "Failed to read xml of Outfit's Accordion Tab from outfit_accordion_tab.xml" << llendl;
- }
- }
- return tab_params;
- }
- //////////////////////////////////////////////////////////////////////////
- class LLOutfitListGearMenu
- {
- public:
- LLOutfitListGearMenu(LLOutfitsList* olist)
- : mOutfitList(olist),
- mMenu(NULL)
- {
- llassert_always(mOutfitList);
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
- registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenu::onWear, this));
- registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenu::onTakeOff, this));
- registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenu::onRename, this));
- registrar.add("Gear.Delete", boost::bind(&LLOutfitsList::removeSelected, mOutfitList));
- registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenu::onCreate, this, _2));
- registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenu::onAdd, this));
- enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenu::onEnable, this, _2));
- enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenu::onVisible, this, _2));
- mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
- "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- llassert(mMenu);
- }
- void updateItemsVisibility()
- {
- if (!mMenu) return;
- bool have_selection = getSelectedOutfitID().notNull();
- mMenu->setItemVisible("sepatator1", have_selection);
- mMenu->setItemVisible("sepatator2", have_selection);
- mMenu->arrangeAndClear(); // update menu height
- }
- LLToggleableMenu* getMenu() { return mMenu; }
- private:
- const LLUUID& getSelectedOutfitID()
- {
- return mOutfitList->getSelectedOutfitUUID();
- }
- LLViewerInventoryCategory* getSelectedOutfit()
- {
- const LLUUID& selected_outfit_id = getSelectedOutfitID();
- if (selected_outfit_id.isNull())
- {
- return NULL;
- }
- LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id);
- return cat;
- }
- void onWear()
- {
- LLViewerInventoryCategory* selected_outfit = getSelectedOutfit();
- if (selected_outfit)
- {
- LLAppearanceMgr::instance().wearInventoryCategory(
- selected_outfit, /*copy=*/ FALSE, /*append=*/ FALSE);
- }
- }
- void onAdd()
- {
- const LLUUID& selected_id = getSelectedOutfitID();
- if (selected_id.notNull())
- {
- LLAppearanceMgr::getInstance()->addCategoryToCurrentOutfit(selected_id);
- }
- }
- void onTakeOff()
- {
- // Take off selected outfit.
- const LLUUID& selected_outfit_id = getSelectedOutfitID();
- if (selected_outfit_id.notNull())
- {
- LLAppearanceMgr::instance().takeOffOutfit(selected_outfit_id);
- }
- }
- void onRename()
- {
- const LLUUID& selected_outfit_id = getSelectedOutfitID();
- if (selected_outfit_id.notNull())
- {
- LLAppearanceMgr::instance().renameOutfit(selected_outfit_id);
- }
- }
- void onCreate(const LLSD& data)
- {
- LLWearableType::EType type = LLWearableType::typeNameToType(data.asString());
- if (type == LLWearableType::WT_NONE)
- {
- llwarns << "Invalid wearable type" << llendl;
- return;
- }
- LLAgentWearables::createWearable(type, true);
- }
- bool 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 onVisible(LLSD::String param)
- {
- const LLUUID& selected_outfit_id = getSelectedOutfitID();
- if (selected_outfit_id.isNull()) // no selection or invalid outfit selected
- {
- return false;
- }
- // *TODO This condition leads to menu item behavior inconsistent with
- // "Wear" button behavior and should be modified or removed.
- bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == selected_outfit_id;
- if ("wear" == param)
- {
- return !is_worn;
- }
- return true;
- }
- LLOutfitsList* mOutfitList;
- LLToggleableMenu* mMenu;
- };
- //////////////////////////////////////////////////////////////////////////
- class LLOutfitContextMenu : public LLListContextMenu
- {
- public:
- LLOutfitContextMenu(LLOutfitsList* outfit_list)
- : LLListContextMenu(),
- mOutfitList(outfit_list)
- {}
- protected:
- /* virtual */ LLContextMenu* 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(&LLOutfitsList::removeSelected, mOutfitList));
- 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 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 onVisible(LLSD::String param)
- {
- LLUUID outfit_cat_id = mUUIDs.back();
- bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id;
- if ("edit" == param)
- {
- return is_worn;
- }
- else if ("wear_replace" == param)
- {
- return !is_worn;
- }
- else if ("delete" == param)
- {
- return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id);
- }
- return true;
- }
- static void editOutfit()
- {
- LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit"));
- }
- static void renameOutfit(const LLUUID& outfit_cat_id)
- {
- LLAppearanceMgr::instance().renameOutfit(outfit_cat_id);
- }
- private:
- LLOutfitsList* mOutfitList;
- };
- //////////////////////////////////////////////////////////////////////////
- static LLRegisterPanelClassWrapper<LLOutfitsList> t_outfits_list("outfits_list");
- LLOutfitsList::LLOutfitsList()
- : LLPanelAppearanceTab()
- , mAccordion(NULL)
- , mListCommands(NULL)
- , mIsInitialized(false)
- , mItemSelected(false)
- {
- mCategoriesObserver = new LLInventoryCategoriesObserver();
- mGearMenu = new LLOutfitListGearMenu(this);
- mOutfitMenu = new LLOutfitContextMenu(this);
- }
- LLOutfitsList::~LLOutfitsList()
- {
- delete mGearMenu;
- delete mOutfitMenu;
- if (gInventory.containsObserver(mCategoriesObserver))
- {
- gInventory.removeObserver(mCategoriesObserver);
- }
- delete mCategoriesObserver;
- }
- BOOL LLOutfitsList::postBuild()
- {
- mAccordion = getChild<LLAccordionCtrl>("outfits_accordion");
- mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR);
- LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn");
- menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenu::updateItemsVisibility, mGearMenu));
- menu_gear_btn->setMenu(mGearMenu->getMenu());
- return TRUE;
- }
- //virtual
- void LLOutfitsList::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(&LLOutfitsList::refreshList, 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(&LLOutfitsList::highlightBaseOutfit, this));
- LLOutfitObserver::instance().addBOFReplacedCallback(boost::bind(&LLOutfitsList::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);
- highlightBaseOutfit();
- mIsInitialized = true;
- }
- LLAccordionCtrlTab* selected_tab = mAccordion->getSelectedTab();
- if (!selected_tab) return;
- // Pass focus to the selected outfit tab.
- selected_tab->showAndFocusHeader();
- }
- void LLOutfitsList::refreshList(const LLUUID& category_id)
- {
- 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);
- uuid_vec_t vadded;
- uuid_vec_t vremoved;
- // Create added and removed items vectors.
- computeDifference(cat_array, vadded, vremoved);
- // Handle added tabs.
- for (uuid_vec_t::const_iterator iter = vadded.begin();
- iter != vadded.end();
- ++iter)
- {
- const LLUUID cat_id = (*iter);
- LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
- if (!cat) continue;
- std::string name = cat->getName();
- outfit_accordion_tab_params tab_params(get_accordion_tab_params());
- LLAccordionCtrlTab* tab = LLUICtrlFactory::create<LLAccordionCtrlTab>(tab_params);
- if (!tab) continue;
- LLWearableItemsList* wearable_list = LLUICtrlFactory::create<LLWearableItemsList>(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<LLWearableItemsList>("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();
- continue;
- }
- // Map the new tab with outfit category UUID.
- mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab));
- tab->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onAccordionTabRightClick, this,
- _1, _2, _3, cat_id));
- // Setting tab focus callback to monitor currently selected outfit.
- tab->setFocusReceivedCallback(boost::bind(&LLOutfitsList::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::onSelectionChange, this, _1));
- // Setting list refresh callback to apply filter on list change.
- list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onFilteredWearableItemsListRefresh, 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 and
- // open it to show matched items if any.
- if (!sFilterSubString.empty())
- {
- tab->notifyChildren(LLSD().with("action","store_state"));
- tab->setDisplayChildren(true);
- // 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(sFilterSubString);
- }
- }
- // Handle removed tabs.
- for (uuid_vec_t::const_iterator iter=vremoved.begin(); iter != vremoved.end(); ++iter)
- {
- outfits_map_t::iterator outfits_iter = mOutfitsMap.find((*iter));
- 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();
- }
- }
- }
- // Get changed items from inventory model and update outfit tabs
- // which might have been renamed.
- const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs();
- for (LLInventoryModel::changed_items_t::const_iterator items_iter = changed_items.begin();
- items_iter != changed_items.end();
- ++items_iter)
- {
- updateOutfitTab(*items_iter);
- }
- mAccordion->sort();
- }
- void LLOutfitsList::highlightBaseOutfit()
- {
- // id of base outfit
- LLUUID base_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID();
- if (base_id != mHighlightedOutfitUUID)
- {
- if (mOutfitsMap[mHighlightedOutfitUUID])
- {
- mOutfitsMap[mHighlightedOutfitUUID]->setTitleFontStyle("NORMAL");
- mOutfitsMap[mHighlightedOutfitUUID]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor"));
- }
- mHighlightedOutfitUUID = base_id;
- }
- if (mOutfitsMap[base_id])
- {
- mOutfitsMap[base_id]->setTitleFontStyle("BOLD");
- mOutfitsMap[base_id]->setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor"));
- }
- }
- void LLOutfitsList::onSelectionChange(LLUICtrl* ctrl)
- {
- LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(ctrl);
- if (!list) return;
- LLViewerInventoryItem *item = gInventory.getItem(list->getSelectedUUID());
- if (!item) return;
- changeOutfitSelection(list, item->getParentUUID());
- }
- void LLOutfitsList::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::removeSelected()
- {
- LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitsList::onOutfitsRemovalConfirmation, this, _1, _2));
- }
- void LLOutfitsList::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response)
- {
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0) return; // canceled
- if (mSelectedOutfitUUID.notNull())
- {
- remove_category(&gInventory, mSelectedOutfitUUID);
- }
- }
- void LLOutfitsList::setSelectedOutfitByUUID(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<LLWearableItemsList*>(tab->getAccordionView());
- if (!list) continue;
- tab->setFocus(TRUE);
- changeOutfitSelection(list, outfit_uuid);
- tab->setDisplayChildren(true);
- }
- }
- }
- // virtual
- void LLOutfitsList::setFilterSubString(const std::string& string)
- {
- applyFilter(string);
- sFilterSubString = string;
- }
- // virtual
- bool LLOutfitsList::isActionEnabled(const LLSD& userdata)
- {
- if (mSelectedOutfitUUID.isNull()) return false;
- const std::string command_name = userdata.asString();
- if (command_name == "delete")
- {
- return !mItemSelected && 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);
- }
- }
- boost::signals2::connection LLOutfitsList::setSelectionChangeCallback(selection_change_callback_t cb)
- {
- return mSelectionChangeSignal.connect(cb);
- }
- bool LLOutfitsList::hasItemSelected()
- {
- return mItemSelected;
- }
- //////////////////////////////////////////////////////////////////////////
- // Private methods
- //////////////////////////////////////////////////////////////////////////
- void LLOutfitsList::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;
- // 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);
- }
- LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved);
- }
- void LLOutfitsList::updateOutfitTab(const LLUUID& category_id)
- {
- outfits_map_t::iterator outfits_iter = mOutfitsMap.find(category_id);
- if (outfits_iter != mOutfitsMap.end())
- {
- LLViewerInventoryCategory *cat = gInventory.getCategory(category_id);
- if (!cat) return;
- std::string name = cat->getName();
- // 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;
- setSelectedOutfitUUID(category_id);
- }
- void LLOutfitsList::changeOutfitSelection(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));
- setSelectedOutfitUUID(category_id);
- }
- void LLOutfitsList::setSelectedOutfitUUID(const LLUUID& category_id)
- {
- mSelectionChangeSignal(mSelectedOutfitUUID = category_id);
- }
- void LLOutfitsList::deselectOutfit(const LLUUID& category_id)
- {
- // Remove selected lists map entry.
- mSelectedListsMap.erase(category_id);
- // Reset selection if the outfit is selected.
- if (category_id == mSelectedOutfitUUID)
- {
- setSelectedOutfitUUID(LLUUID::null);
- }
- }
- void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID& category_id)
- {
- // Try restoring outfit selection after filtering.
- if (mAccordion->getSelectedTab() == tab)
- {
- setSelectedOutfitUUID(category_id);
- }
- }
- void LLOutfitsList::onFilteredWearableItemsListRefresh(LLUICtrl* ctrl)
- {
- if (!ctrl || sFilterSubString.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<LLWearableItemsList*>(tab->getAccordionView());
- if (list != ctrl) continue;
- applyFilterToTab(iter->first, tab, sFilterSubString);
- }
- }
- void LLOutfitsList::applyFilter(const std::string& new_filter_substring)
- {
- mAccordion->setFilterSubString(new_filter_substring);
- for (outfits_map_t::iterator
- iter = mOutfitsMap.begin(),
- iter_end = mOutfitsMap.end();
- iter != iter_end; ++iter)
- {
- LLAccordionCtrlTab* tab = iter->second;
- if (!tab) continue;
- bool more_restrictive = sFilterSubString.size() < new_filter_substring.size() && !new_filter_substring.substr(0, sFilterSubString.size()).compare(sFilterSubString);
- // Restore tab visibility in case of less restrictive filter
- // to compare it with updated string if it was previously hidden.
- if (!more_restrictive)
- {
- tab->setVisible(TRUE);
- }
- LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
- if (list)
- {
- list->setFilterSubString(new_filter_substring);
- }
- if(sFilterSubString.empty() && !new_filter_substring.empty())
- {
- //store accordion tab state when filter is not empty
- tab->notifyChildren(LLSD().with("action","store_state"));
- }
- if (!new_filter_substring.empty())
- {
- applyFilterToTab(iter->first, tab, new_filter_substring);
- }
- else
- {
- // 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, iter->first);
- }
- }
- mAccordion->arrange();
- }
- void LLOutfitsList::applyFilterToTab(
- const LLUUID& category_id,
- LLAccordionCtrlTab* tab,
- const std::string& filter_substring)
- {
- if (!tab) return;
- LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(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 visible 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);
- }
- if (tab->getVisible())
- {
- // Open tab if it has passed the filter.
- tab->setDisplayChildren(true);
- }
- else
- {
- // Set force refresh flag to refresh not visible list
- // when some changes occur in it.
- list->setForceRefresh(true);
- }
- }
- bool LLOutfitsList::canWearSelected()
- {
- uuid_vec_t selected_items;
- getSelectedItemsUUIDs(selected_items);
- 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;
- }
- }
- // All selected items can be worn.
- return true;
- }
- void LLOutfitsList::onAccordionTabRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id)
- {
- LLAccordionCtrlTab* tab = dynamic_cast<LLAccordionCtrlTab*>(ctrl);
- if(mOutfitMenu && is_tab_header_clicked(tab, y) && cat_id.notNull())
- {
- // Focus tab header to trigger tab selection change.
- LLUICtrl* header = tab->findChild<LLUICtrl>("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);
- }
- }
- 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<LLWearableItemsList*>(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.
- for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
- iter != item_array.end();
- ++iter)
- {
- vnew.push_back((*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;
- for (outfits_map_t::iterator iter = mOutfitsMap.begin();
- iter != mOutfitsMap.end();
- ++iter)
- {
- LLAccordionCtrlTab* tab = iter->second;
- if (!tab) continue;
- LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
- if (!list) continue;
- // 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());
- // Every list updates the labels of changed items or
- // the links that point to these items.
- list->updateChangedItems(vadded);
- }
- }
- 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;
- }
- // EOF