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 LLPanelWearing::onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y) { LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(ctrl); if (!list) return; uuid_vec_t selected_uuids; list->getSelectedUUIDs(selected_uuids); mContextMenu->show(ctrl, selected_uuids, x, y); }
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::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); } }
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); } }
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); } }
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::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. // <FS:ND> FIRE-6958/VWR-2862; Make sure to only collect folders of type FT_OUTFIT class ndOutfitsCollector: public LLIsType { public: ndOutfitsCollector() : LLIsType( LLAssetType::AT_CATEGORY ) { } virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) { if( !LLIsType::operator()( cat, item ) ) return false; if( cat && LLFolderType::FT_OUTFIT == cat->getPreferredType() ) return true; return false; } }; // LLIsType is_category(LLAssetType::AT_CATEGORY); ndOutfitsCollector is_category; // </FS:ND> 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); // <FS:ND> FIRE-6958/VWR-2862; Handle large amounts of outfits, write a least a warning into the logs. if( vadded.size() > 128 ) LL_WARNS() << "Large amount of outfits found: " << vadded.size() << " this may cause hangs and disconnects" << LL_ENDL; U32 nCap = gSavedSettings.getU32( "FSDisplaySavedOutfitsCap" ); if( nCap && nCap < vadded.size() ) { vadded.resize( nCap ); LL_WARNS() << "Capped outfits to " << nCap << " due to debug setting FSDisplaySavedOutfitsCap" << LL_ENDL; } // </FS:ND> // <FS:Ansariel> FIRE-12939: Add outfit count to outfits list getChild<LLTextBox>("OutfitcountText")->setTextArg("COUNT", llformat("%d", cat_array.size())); // 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); // <FS:ND> Calling this when there's a lot of outfits causes horrible perfomance and disconnects, due to arrange eating so many cpu cycles. // mAccordion->addCollapsibleCtrl(tab); mAccordion->addCollapsibleCtrl(tab, false ); // </FS:ND> // 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); } } // <FS:ND> We called mAccordion->addCollapsibleCtrl with false as second paramter and did not let it arrange itself each time. Do this here after all is said and done. mAccordion->arrange(); // </FS:ND> // 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(); }