diff options
Diffstat (limited to 'indra/newview/lloutfitslist.cpp')
-rw-r--r-- | indra/newview/lloutfitslist.cpp | 994 |
1 files changed, 92 insertions, 902 deletions
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 3eda077b87..1215272685 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -2,25 +2,30 @@ * @file lloutfitslist.cpp * @brief List of agent's outfits for My Appearance side panel. * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * * 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 + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ @@ -31,373 +36,76 @@ // llcommon #include "llcommonutils.h" +// llcommon +#include "llcommonutils.h" + #include "llaccordionctrl.h" #include "llaccordionctrltab.h" -#include "llagentwearables.h" -#include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" -#include "lllistcontextmenu.h" -#include "llnotificationsutil.h" -#include "lloutfitobserver.h" -#include "llsidetray.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; -} - -////////////////////////////////////////////////////////////////////////// - -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(&LLOutfitListGearMenu::onDelete, this)); - 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(&LLOutfitsList::isActionEnabled, mOutfitList, _2)); - enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenu::onVisible, this, _2)); - - mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( - "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - llassert(mMenu); - } - - void show(LLView* spawning_view) - { - if (!mMenu) return; - - updateItemsVisibility(); - mMenu->buildDrawLabels(); - mMenu->updateParent(LLMenuGL::sMenuContainer); - S32 menu_x = 0; - S32 menu_y = spawning_view->getRect().getHeight() + mMenu->getRect().getHeight(); - LLMenuGL::showPopup(spawning_view, mMenu, menu_x, menu_y); - } - - 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 - } - -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 items if there are any - if (mOutfitList->hasItemSelected()) - { - uuid_vec_t selected_uuids; - mOutfitList->getSelectedItemsUUIDs(selected_uuids); - - for (uuid_vec_t::const_iterator it=selected_uuids.begin(); it != selected_uuids.end(); ++it) - { - if (get_is_item_worn(*it)) - { - LLAppearanceMgr::instance().removeItemFromAvatar(*it); - } - } - } - else // or take off the whole selected outfit if no items specified. - { - 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 onDelete() - { - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.notNull()) - { - remove_category(&gInventory, 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 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; - LLMenuGL* mMenu; -}; - -////////////////////////////////////////////////////////////////////////// - -class LLOutfitContextMenu : public LLListContextMenu -{ -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(deleteOutfit, 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 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() - { - LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit")); - } - - static void renameOutfit(const LLUUID& outfit_cat_id) - { - LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); - } - - static void deleteOutfit(const LLUUID& outfit_cat_id) - { - remove_category(&gInventory, outfit_cat_id); - } -}; - -////////////////////////////////////////////////////////////////////////// - static LLRegisterPanelClassWrapper<LLOutfitsList> t_outfits_list("outfits_list"); LLOutfitsList::LLOutfitsList() - : LLPanelAppearanceTab() + : LLPanel() , mAccordion(NULL) , mListCommands(NULL) - , mIsInitialized(false) - , mItemSelected(false) { mCategoriesObserver = new LLInventoryCategoriesObserver(); + gInventory.addObserver(mCategoriesObserver); - mGearMenu = new LLOutfitListGearMenu(this); - mOutfitMenu = new LLOutfitContextMenu(); + gInventory.addObserver(this); } LLOutfitsList::~LLOutfitsList() { - delete mGearMenu; - delete mOutfitMenu; - if (gInventory.containsObserver(mCategoriesObserver)) { gInventory.removeObserver(mCategoriesObserver); delete mCategoriesObserver; } + + if (gInventory.containsObserver(this)) + { + gInventory.removeObserver(this); + } } BOOL LLOutfitsList::postBuild() { mAccordion = getChild<LLAccordionCtrl>("outfits_accordion"); - mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); return TRUE; } //virtual -void LLOutfitsList::onOpen(const LLSD& /*info*/) +void LLOutfitsList::changed(U32 mask) { - 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; + if (!gInventory.isInventoryUsable()) + return; - gInventory.addObserver(mCategoriesObserver); + const LLUUID outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + LLViewerInventoryCategory* category = gInventory.getCategory(outfits); + if (!category) + return; - // Start observing changes in "My Outfits" category. - mCategoriesObserver->addCategory(outfits, + // 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(); + // 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; - } + // This observer is used to start the initial outfits fetch + // when inventory becomes usable. It is no longer needed because + // "My Outfits" category is now observed by + // LLInventoryCategoriesObserver. + gInventory.removeObserver(this); } void LLOutfitsList::refreshList(const LLUUID& category_id) @@ -427,57 +135,30 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) { const LLUUID cat_id = (*iter); LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); - if (!cat) continue; + if (!cat) + continue; std::string name = cat->getName(); static LLXMLNodePtr accordionXmlNode = getAccordionTabXMLNode(); - LLAccordionCtrlTab* tab = LLUICtrlFactory::defaultBuilder<LLAccordionCtrlTab>(accordionXmlNode, NULL, NULL); - tab->setName(name); - tab->setTitle(name); + accordionXmlNode->setAttributeString("name", name); + accordionXmlNode->setAttributeString("title", name); + LLAccordionCtrlTab* tab = LLUICtrlFactory::defaultBuilder<LLAccordionCtrlTab>(accordionXmlNode, NULL, NULL); // *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 - if (tab != NULL) - { - 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)); + // Start observing the new outfit category. + LLWearableItemsList* list = tab->getChild<LLWearableItemsList>("wearable_items_list"); + mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id)); - list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3)); + // Setting drop down callback to monitor currently selected outfit. + tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::onTabExpandedCollapsed, this, list)); // Fetch the new outfit contents. cat->fetch(); @@ -485,50 +166,23 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) // 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) + 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); + // 1. Remove outfit accordion tab from accordion. + mAccordion->removeCollapsibleCtrl(outfits_iter->second); - // 2. Remove the outfit from selection. - deselectOutfit(outfit_id); + // 2. Remove outfit category from observer to stop monitoring its changes. + mCategoriesObserver->removeCategory(outfits_iter->first); // 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(); - } } } @@ -542,185 +196,50 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) 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() -{ - if (mSelectedOutfitUUID.notNull()) - { - remove_category(&gInventory, mSelectedOutfitUUID); - } + mAccordion->arrange(); } -void LLOutfitsList::setSelectedOutfitByUUID(const LLUUID& outfit_uuid) +void LLOutfitsList::updateOutfitTab(const LLUUID& category_id) { - for (outfits_map_t::iterator iter = mOutfitsMap.begin(); - iter != mOutfitsMap.end(); - ++iter) + outfits_map_t::iterator outfits_iter = mOutfitsMap.find(category_id); + if (outfits_iter != mOutfitsMap.end()) { - 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; -} + LLViewerInventoryCategory *cat = gInventory.getCategory(category_id); + if (!cat) + return; -// virtual -bool LLOutfitsList::isActionEnabled(const LLSD& userdata) -{ - if (mSelectedOutfitUUID.isNull()) return false; + std::string name = cat->getName(); - 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()) + // Update tab name with the new category name. + LLAccordionCtrlTab* tab = outfits_iter->second; + if (tab) { - return false; + tab->setName(name); } - if (hasItemSelected()) + // Update tab title with the new category name using textbox + // in accordion tab header. + LLTextBox* tab_title = tab->findChild<LLTextBox>("dd_textbox"); + if (tab_title) { - return canWearSelected(); + tab_title->setText(name); } - - // 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; } -// virtual -void LLOutfitsList::showGearMenu(LLView* spawning_view) +void LLOutfitsList::onTabExpandedCollapsed(LLWearableItemsList* list) { - if (!mGearMenu) return; - mGearMenu->show(spawning_view); -} - -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); + if (!list) + return; - S32 prev_size = selected_uuids.size(); - selected_uuids.resize(prev_size + uuids.size()); - std::copy(uuids.begin(), uuids.end(), selected_uuids.begin() + prev_size); - } + // TODO: Add outfit selection handling. } -boost::signals2::connection LLOutfitsList::setSelectionChangeCallback(selection_change_callback_t cb) +void LLOutfitsList::setFilterSubString(const std::string& string) { - return mSelectionChangeSignal.connect(cb); + mFilterSubString = string; } -bool LLOutfitsList::hasItemSelected() -{ - return mItemSelected; -} ////////////////////////////////////////////////////////////////////////// // Private methods @@ -764,333 +283,4 @@ void LLOutfitsList::computeDifference( 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::canTakeOffSelected() -{ - uuid_vec_t selected_uuids; - getSelectedItemsUUIDs(selected_uuids); - - LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false); - - for (uuid_vec_t::const_iterator it=selected_uuids.begin(); it != selected_uuids.end(); ++it) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if (!item) continue; - - if (is_worn(NULL, item)) return true; - } - return false; -} - -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::changed_items_t changed_linked_items; - - const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); - for (LLInventoryModel::changed_items_t::const_iterator iter = changed_items.begin(); - iter != changed_items.end(); - ++iter) - { - LLViewerInventoryItem* item = gInventory.getItem(*iter); - if (item) - { - // From gInventory we get the UUIDs of new links added to COF - // or removed from 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 all items or links that point to same base items in wearable - // items lists and update their worn state there. - changed_linked_items.insert(item->getLinkedUUID()); - } - } - - 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; - - // Every list updates the labels of changed items or - // the links that point to these items. - list->updateChangedItems(changed_linked_items); - } -} - -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 |