diff options
author | Tofu Linden <tofu.linden@lindenlab.com> | 2010-04-30 10:10:16 +0100 |
---|---|---|
committer | Tofu Linden <tofu.linden@lindenlab.com> | 2010-04-30 10:10:16 +0100 |
commit | 6f339d5e9eb0a43f70effdf069d414e0ee7fff2c (patch) | |
tree | c8070823806fbeb68ea0813d9a766a1b1db458f2 /indra/newview | |
parent | 4f46e777a501ff0bfedaf657ac6decdeba8acbe5 (diff) | |
parent | d597a7e36a0a8c4cbee70b053c41aa01afeab6b5 (diff) |
merge from viewer-trunk
Diffstat (limited to 'indra/newview')
33 files changed, 651 insertions, 117 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4c0d47ced3..c5602d1bcc 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5596,6 +5596,19 @@ <key>Value</key> <integer>8</integer> </map> + + <key>PluginUseReadThread</key> + <map> + <key>Comment</key> + <string>Use a separate thread to read incoming messages from plugins</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>PrecachingDelay</key> <map> <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7bfe6a46c9..8f14b8d782 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1548,6 +1548,9 @@ bool LLAppViewer::cleanup() LLViewerMedia::saveCookieFile(); + // Stop the plugin read thread if it's running. + LLPluginProcessParent::setUseReadThread(false); + llinfos << "Shutting down Threads" << llendflush; // Let threads finish diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 4f9434030f..fafa315a59 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -243,3 +243,19 @@ void LLChannelManager::killToastsFromChannel(const LLUUID& channel_id, const LLS } } +// static +LLNotificationsUI::LLScreenChannel* LLChannelManager::getNotificationScreenChannel() +{ + LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*> + (LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); + + if (channel == NULL) + { + llwarns << "Can't find screen channel by NotificationChannelUUID" << llendl; + llassert(!"Can't find screen channel by NotificationChannelUUID"); + } + + return channel; +} + diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h index c2be39122f..8c725f2660 100644 --- a/indra/newview/llchannelmanager.h +++ b/indra/newview/llchannelmanager.h @@ -114,6 +114,11 @@ public: */ void killToastsFromChannel(const LLUUID& channel_id, const LLScreenChannel::Matcher& matcher); + /** + * Returns notification screen channel. + */ + static LLNotificationsUI::LLScreenChannel* getNotificationScreenChannel(); + private: LLScreenChannel* createChannel(LLChannelManager::Params& p); diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index a2a1dd504a..b8222ebb18 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -204,10 +204,10 @@ void LLCOFWearables::addClothingTypesDummies(const LLAppearanceMgr::wearables_by U32 size = clothing_by_type[type].size(); if (size) continue; - //*TODO create dummy item panel - - //*TODO add dummy item panel -> mClothing->addItem(dummy_item_panel, item->getUUID(), ADD_BOTTOM, false); - + EWearableType w_type = static_cast<EWearableType>(type); + LLPanelInventoryListItemBase* item_panel = LLPanelDummyClothingListItem::create(w_type); + if(!item_panel) continue; + mClothing->addItem(item_panel, LLUUID::null, ADD_BOTTOM, false); addWearableTypeSeparator(mClothing); } } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 19dbc564d1..c0cc3f1985 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -53,6 +53,7 @@ #include "llsyswellwindow.h" #include "lltrans.h" #include "llchathistory.h" +#include "llnotifications.h" #include "llviewerwindow.h" #include "llvoicechannel.h" #include "lltransientfloatermgr.h" @@ -371,6 +372,8 @@ void LLIMFloater::onSlide() //static LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { + closeHiddenIMToasts(); + if (!gIMMgr->hasSession(session_id)) return NULL; if(!isChatMultiTab()) @@ -1084,6 +1087,26 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) } // static +void LLIMFloater::closeHiddenIMToasts() +{ + class IMToastMatcher: public LLNotificationsUI::LLScreenChannel::Matcher + { + public: + bool matches(const LLNotificationPtr notification) const + { + // "notifytoast" type of notifications is reserved for IM notifications + return "notifytoast" == notification->getType(); + } + }; + + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); + if (channel != NULL) + { + channel->closeHiddenToasts(IMToastMatcher()); + } +} + +// static bool LLIMFloater::isChatMultiTab() { // Restart is required in order to change chat window type. diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 763dd5655b..f9dd8b9b85 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -148,6 +148,8 @@ private: // Remove the "User is typing..." indicator. void removeTypingIndicator(const LLIMInfo* im_info = NULL); + static void closeHiddenIMToasts(); + LLPanelChatControlPanel* mControlPanel; LLUUID mSessionID; S32 mLastMessageIndex; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 04556acf67..ea43670da0 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -854,21 +854,7 @@ void LLInvFVBridge::changeItemParent(LLInventoryModel* model, const LLUUID& new_parent_id, BOOL restamp) { - if (item->getParentUUID() != new_parent_id) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - new_item->setParent(new_parent_id); - new_item->updateParentOnServer(restamp); - model->updateItem(new_item); - model->notifyObservers(); - } + change_item_parent(model, item, new_parent_id, restamp); } // static diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 449b1b5b4d..6c7251579d 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -585,3 +585,26 @@ BOOL get_is_item_worn(const LLUUID& id) } return FALSE; } + + +void change_item_parent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + BOOL restamp) +{ + if (item->getParentUUID() != new_parent_id) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item->setParent(new_parent_id); + new_item->updateParentOnServer(restamp); + model->updateItem(new_item); + model->notifyObservers(); + } +} diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index e3cd988e39..6f373f7392 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -310,6 +310,12 @@ LLUIImagePtr get_item_icon(LLAssetType::EType asset_type, // Is this item or its baseitem is worn, attached, etc... BOOL get_is_item_worn(const LLUUID& id); + +void change_item_parent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + BOOL restamp); + #endif // LL_LLINVENTORYFUNCTIONS_H diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp index 3d8cb6dfe8..8dfdb0788a 100644 --- a/indra/newview/llinventoryitemslist.cpp +++ b/indra/newview/llinventoryitemslist.cpp @@ -65,14 +65,8 @@ LLPanelInventoryListItemBase* LLPanelInventoryListItemBase::create(LLViewerInven void LLPanelInventoryListItemBase::updateItem() { - if (mItemIcon.notNull()) - mIcon->setImage(mItemIcon); - - LLTextUtil::textboxSetHighlightedVal( - mTitle, - LLStyle::Params(), - mItem->getName(), - mHighlightedText); + setIconImage(mIconImage); + setTitle(mItem->getName(), mHighlightedText); } void LLPanelInventoryListItemBase::addWidgetToLeftSide(const std::string& name, bool show_widget/* = true*/) @@ -122,9 +116,10 @@ void LLPanelInventoryListItemBase::setShowWidget(LLUICtrl* ctrl, bool show) BOOL LLPanelInventoryListItemBase::postBuild() { - // Inheritors need to call base implementation - mIcon = getChild<LLIconCtrl>("item_icon"); - mTitle = getChild<LLTextBox>("item_name"); + setIconCtrl(getChild<LLIconCtrl>("item_icon")); + setTitleCtrl(getChild<LLTextBox>("item_name")); + + mIconImage = get_item_icon(mItem->getType(), mItem->getInventoryType(), mItem->getFlags(), FALSE); updateItem(); @@ -156,13 +151,12 @@ void LLPanelInventoryListItemBase::onMouseLeave(S32 x, S32 y, MASK mask) LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem* item) : LLPanel() , mItem(item) -, mIcon(NULL) -, mTitle(NULL) +, mIconCtrl(NULL) +, mTitleCtrl(NULL) , mWidgetSpacing(WIDGET_SPACING) , mLeftWidgetsWidth(0) , mRightWidgetsWidth(0) { - mItemIcon = get_item_icon(mItem->getType(), mItem->getInventoryType(), mItem->getFlags(), FALSE); } void LLPanelInventoryListItemBase::init() @@ -197,6 +191,24 @@ void LLPanelInventoryListItemBase::reshapeWidgets() reshapeMiddleWidgets(); } +void LLPanelInventoryListItemBase::setIconImage(const LLUIImagePtr& image) +{ + if(image) + { + mIconImage = image; + mIconCtrl->setImage(mIconImage); + } +} + +void LLPanelInventoryListItemBase::setTitle(const std::string& title, const std::string& highlit_text) +{ + LLTextUtil::textboxSetHighlightedVal( + mTitleCtrl, + LLStyle::Params(), + title, + highlit_text); +} + void LLPanelInventoryListItemBase::reshapeLeftWidgets() { S32 widget_left = 0; @@ -246,16 +258,16 @@ void LLPanelInventoryListItemBase::reshapeRightWidgets() void LLPanelInventoryListItemBase::reshapeMiddleWidgets() { - LLRect icon_rect(mIcon->getRect()); + LLRect icon_rect(mIconCtrl->getRect()); icon_rect.setLeftTopAndSize(mLeftWidgetsWidth + getWidgetSpacing(), icon_rect.mTop, icon_rect.getWidth(), icon_rect.getHeight()); - mIcon->setShape(icon_rect); + mIconCtrl->setShape(icon_rect); S32 name_left = icon_rect.mRight + getWidgetSpacing(); S32 name_right = getLocalRect().getWidth() - mRightWidgetsWidth - getWidgetSpacing(); - LLRect name_rect(mTitle->getRect()); + LLRect name_rect(mTitleCtrl->getRect()); name_rect.set(name_left, name_rect.mTop, name_right, name_rect.mBottom); - mTitle->setShape(name_rect); + mTitleCtrl->setShape(name_rect); } //////////////////////////////////////////////////////////////////////////////// @@ -356,17 +368,18 @@ void LLInventoryItemsList::addNewItem(LLViewerInventoryItem* item) if (!item) { llwarns << "No inventory item. Couldn't create flat list item." << llendl; - llassert(!"No inventory item. Couldn't create flat list item."); + llassert(item != NULL); } LLPanelInventoryListItemBase *list_item = LLPanelInventoryListItemBase::create(item); if (!list_item) return; - if (!addItem(list_item, item->getUUID())) + bool is_item_added = addItem(list_item, item->getUUID()); + if (!is_item_added) { llwarns << "Couldn't add flat list item." << llendl; - llassert(!"Couldn't add flat list item."); + llassert(is_item_added); } } diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h index 15f04c79a9..152aafbd7e 100644 --- a/indra/newview/llinventoryitemslist.h +++ b/indra/newview/llinventoryitemslist.h @@ -125,6 +125,11 @@ protected: */ virtual void init(); + /** setter for mIconCtrl */ + void setIconCtrl(LLIconCtrl* icon) { mIconCtrl = icon; } + /** setter for MTitleCtrl */ + void setTitleCtrl(LLTextBox* tb) { mTitleCtrl = tb; } + void setLeftWidgetsWidth(S32 width) { mLeftWidgetsWidth = width; } void setRightWidgetsWidth(S32 width) { mRightWidgetsWidth = width; } @@ -139,6 +144,12 @@ protected: */ virtual void reshapeWidgets(); + /** set wearable type icon image */ + void setIconImage(const LLUIImagePtr& image); + + /** Set item title - inventory item name usually */ + void setTitle(const std::string& title, const std::string& highlit_text); + private: /** reshape left side widgets @@ -155,10 +166,10 @@ private: LLViewerInventoryItem* mItem; - LLIconCtrl* mIcon; - LLTextBox* mTitle; + LLIconCtrl* mIconCtrl; + LLTextBox* mTitleCtrl; - LLUIImagePtr mItemIcon; + LLUIImagePtr mIconImage; std::string mHighlightedText; widget_array_t mLeftSideWidgets; diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 214b5d317a..86147d65e6 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -215,7 +215,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask) void fetch_items_from_llsd(const LLSD& items_llsd) { - if (!items_llsd.size()) return; + if (!items_llsd.size() || gDisconnected) return; LLSD body; body[0]["cap_name"] = "FetchInventory"; body[1]["cap_name"] = "FetchLib"; @@ -235,6 +235,11 @@ void fetch_items_from_llsd(const LLSD& items_llsd) for (S32 i=0; i<body.size(); i++) { + if(!gAgent.getRegion()) + { + llwarns<<"Agent's region is null"<<llendl; + break; + } if (0 >= body[i].size()) continue; std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); @@ -664,36 +669,87 @@ void LLInventoryCategoriesObserver::changed(U32 mask) if (!category) continue; - S32 version = category->getVersion(); - if (version != (*iter).second.mVersion) + const S32 version = category->getVersion(); + const S32 expected_num_descendents = category->getDescendentCount(); + if ((version == LLViewerInventoryCategory::VERSION_UNKNOWN) || + (expected_num_descendents == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)) { - // Update category version in map. - (*iter).second.mVersion = version; - (*iter).second.mCallback(); + continue; + } + + // Check number of known descendents to find out whether it has changed. + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf((*iter).first, cats, items); + if (!cats || !items) + { + llwarns << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << llendl; + // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean + // that the cat just doesn't have any items or subfolders). + // Unrecoverable, so just skip this category. + + llassert(cats != NULL && items != NULL); + } + const S32 current_num_known_descendents = cats->count() + items->count(); + + LLCategoryData cat_data = (*iter).second; + + // If category version or descendents count has changed + // update category data in mCategoryMap and fire a callback. + if (version != cat_data.mVersion || current_num_known_descendents != cat_data.mDescendentsCount) + { + cat_data.mVersion = version; + cat_data.mDescendentsCount = current_num_known_descendents; + + cat_data.mCallback(); } } } -void LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t cb) +bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t cb) { S32 version; + S32 current_num_known_descendents; + bool can_be_added = true; + LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); if (category) { // Inventory category version is used to find out if some changes // to a category have been made. version = category->getVersion(); + + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat_id, cats, items); + if (!cats || !items) + { + llwarns << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << llendl; + // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean + // that the cat just doesn't have any items or subfolders). + // Unrecoverable, so just return "false" meaning that the category can't be observed. + can_be_added = false; + + llassert(cats != NULL && items != NULL); + } + current_num_known_descendents = cats->count() + items->count(); } else { // If category could not be retrieved it might mean that // inventory is unusable at the moment so the category is - // stored with VERSION_UNKNOWN and it may be updated later. + // stored with VERSION_UNKNOWN and DESCENDENT_COUNT_UNKNOWN, + // it may be updated later. version = LLViewerInventoryCategory::VERSION_UNKNOWN; + current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN; + } + + if (can_be_added) + { + mCategoryMap.insert(category_map_value_t(cat_id, LLCategoryData(cb, version, current_num_known_descendents))); } - version = category->getVersion(); - mCategoryMap.insert(category_map_value_t(cat_id, LLCategoryData(cb, version))); + return can_be_added; } void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id) diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index e63b67d2ad..036e6ca40d 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -276,19 +276,28 @@ public: LLInventoryCategoriesObserver() {}; virtual void changed(U32 mask); - void addCategory(const LLUUID& cat_id, callback_t cb); + /** + * Add cat_id to the list of observed categories with a + * callback fired on category being changed. + * + * @return "true" if category was added, "false" if it could + * not be found. + */ + bool addCategory(const LLUUID& cat_id, callback_t cb); void removeCategory(const LLUUID& cat_id); protected: struct LLCategoryData { - LLCategoryData(callback_t cb, S32 version) + LLCategoryData(callback_t cb, S32 version, S32 num_descendents) : mCallback(cb) , mVersion(version) + , mDescendentsCount(num_descendents) {} callback_t mCallback; S32 mVersion; + S32 mDescendentsCount; }; typedef std::map<LLUUID, LLCategoryData> category_map_t; diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 1215272685..b103ec45d0 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -41,6 +41,7 @@ #include "llaccordionctrl.h" #include "llaccordionctrltab.h" +#include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llwearableitemslist.h" @@ -51,6 +52,7 @@ LLOutfitsList::LLOutfitsList() : LLPanel() , mAccordion(NULL) , mListCommands(NULL) + , mSelectedList(NULL) { mCategoriesObserver = new LLInventoryCategoriesObserver(); gInventory.addObserver(mCategoriesObserver); @@ -135,30 +137,37 @@ 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(); - - accordionXmlNode->setAttributeString("name", name); - accordionXmlNode->setAttributeString("title", name); LLAccordionCtrlTab* tab = LLUICtrlFactory::defaultBuilder<LLAccordionCtrlTab>(accordionXmlNode, NULL, NULL); + 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); + continue; + } + // Map the new tab with outfit category UUID. mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab)); - // 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)); + // Setting tab focus callback to monitor currently selected outfit. + tab->setFocusReceivedCallback(boost::bind(&LLOutfitsList::changeOutfitSelection, this, list, cat_id)); - // Setting drop down callback to monitor currently selected outfit. - tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::onTabExpandedCollapsed, this, list)); + // Setting list commit callback to monitor currently selected wearable item. + list->setCommitCallback(boost::bind(&LLOutfitsList::onSelectionChange, this, _1)); // Fetch the new outfit contents. cat->fetch(); @@ -178,10 +187,18 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) // 1. Remove outfit accordion tab from accordion. mAccordion->removeCollapsibleCtrl(outfits_iter->second); + const LLUUID& outfit_id = outfits_iter->first; + // 2. Remove outfit category from observer to stop monitoring its changes. - mCategoriesObserver->removeCategory(outfits_iter->first); + mCategoriesObserver->removeCategory(outfit_id); - // 3. Remove category UUID to accordion tab mapping. + // 3. Reset selection if selected outfit is being removed. + if (mSelectedOutfitUUID == outfit_id) + { + changeOutfitSelection(NULL, LLUUID()); + } + + // 4. Remove category UUID to accordion tab mapping. mOutfitsMap.erase(outfits_iter); } } @@ -199,40 +216,30 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) mAccordion->arrange(); } -void LLOutfitsList::updateOutfitTab(const LLUUID& category_id) +void LLOutfitsList::onSelectionChange(LLUICtrl* ctrl) { - outfits_map_t::iterator outfits_iter = mOutfitsMap.find(category_id); - if (outfits_iter != mOutfitsMap.end()) - { - LLViewerInventoryCategory *cat = gInventory.getCategory(category_id); - if (!cat) - return; + LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(ctrl); + if (!list) return; - std::string name = cat->getName(); + LLViewerInventoryItem *item = gInventory.getItem(list->getSelectedUUID()); + if (!item) return; - // Update tab name with the new category name. - LLAccordionCtrlTab* tab = outfits_iter->second; - if (tab) - { - tab->setName(name); - } - - // 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) - { - tab_title->setText(name); - } - } + changeOutfitSelection(list, item->getParentUUID()); } -void LLOutfitsList::onTabExpandedCollapsed(LLWearableItemsList* list) +void LLOutfitsList::performAction(std::string action) { - if (!list) - return; + LLViewerInventoryCategory* cat = gInventory.getCategory(mSelectedOutfitUUID); + if (!cat) return; - // TODO: Add outfit selection handling. + if ("replaceoutfit" == action) + { + LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, FALSE ); + } + else if ("addtooutfit" == action) + { + LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, TRUE ); + } } void LLOutfitsList::setFilterSubString(const std::string& string) @@ -240,7 +247,6 @@ void LLOutfitsList::setFilterSubString(const std::string& string) mFilterSubString = string; } - ////////////////////////////////////////////////////////////////////////// // Private methods ////////////////////////////////////////////////////////////////////////// @@ -283,4 +289,37 @@ 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::changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) +{ + // Reset selection in previously selected tab + // if a new one is selected. + if (list && mSelectedList && mSelectedList != list) + { + mSelectedList->resetSelection(); + } + + mSelectedList = list; + mSelectedOutfitUUID = category_id; +} + // EOF diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index 2d103ea356..d86cf5a703 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -65,10 +65,9 @@ public: void refreshList(const LLUUID& category_id); - // Update tab displaying outfit identified by category_id. - void updateOutfitTab(const LLUUID& category_id); + void onSelectionChange(LLUICtrl* ctrl); - void onTabExpandedCollapsed(LLWearableItemsList* list); + void performAction(std::string action); void setFilterSubString(const std::string& string); @@ -85,12 +84,24 @@ private: */ void computeDifference(const LLInventoryModel::cat_array_t& vcats, uuid_vec_t& vadded, uuid_vec_t& vremoved); + /** + * Updates tab displaying outfit identified by category_id. + */ + void updateOutfitTab(const LLUUID& category_id); + + /** + * Resets previous selection and stores newly selected list and outfit id. + */ + void changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id); LLInventoryCategoriesObserver* mCategoriesObserver; LLAccordionCtrl* mAccordion; LLPanel* mListCommands; + LLWearableItemsList* mSelectedList; + LLUUID mSelectedOutfitUUID; + std::string mFilterSubString; typedef std::map<LLUUID, LLAccordionCtrlTab*> outfits_map_t; diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 789e85b46f..80964938f5 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -188,19 +188,37 @@ void LLPanelOutfitsInventory::onSearchEdit(const std::string& string) void LLPanelOutfitsInventory::onWearButtonClick() { - LLFolderViewEventListener* listenerp = getCorrectListenerForAction(); - if (listenerp) + // TODO: Remove if/else, add common interface + // for "My Outfits" and "Wearing" tabs. + if (!isCOFPanelActive()) + { + mMyOutfitsPanel->performAction("replaceoutfit"); + } + else { - listenerp->performAction(NULL, "replaceoutfit"); + LLFolderViewEventListener* listenerp = getCorrectListenerForAction(); + if (listenerp) + { + listenerp->performAction(NULL, "replaceoutfit"); + } } } void LLPanelOutfitsInventory::onAdd() { - LLFolderViewEventListener* listenerp = getCorrectListenerForAction(); - if (listenerp) + // TODO: Remove if/else, add common interface + // for "My Outfits" and "Wearing" tabs. + if (!isCOFPanelActive()) + { + mMyOutfitsPanel->performAction("addtooutfit"); + } + else { - listenerp->performAction(NULL, "addtooutfit"); + LLFolderViewEventListener* listenerp = getCorrectListenerForAction(); + if (listenerp) + { + listenerp->performAction(NULL, "addtooutfit"); + } } } diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 75702dc8e5..fb7ac0d86b 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -35,6 +35,7 @@ #include "llpreviewnotecard.h" #include "llinventory.h" +#include "llinventoryfunctions.h" // for change_item_parent() #include "llagent.h" #include "llassetuploadresponders.h" @@ -92,11 +93,17 @@ BOOL LLPreviewNotecard::postBuild() childSetAction("Save", onClickSave, this); childSetVisible("lock", FALSE); + childSetAction("Delete", onClickDelete, this); + childSetEnabled("Delete", false); + const LLInventoryItem* item = getItem(); childSetCommitCallback("desc", LLPreview::onText, this); if (item) + { childSetText("desc", item->getDescription()); + childSetEnabled("Delete", true); + } childSetPrevalidate("desc", &LLTextValidate::validateASCIIPrintableNoPipe); return LLPreview::postBuild(); @@ -374,6 +381,17 @@ void LLPreviewNotecard::onClickSave(void* user_data) } } + +// static +void LLPreviewNotecard::onClickDelete(void* user_data) +{ + LLPreviewNotecard* preview = (LLPreviewNotecard*)user_data; + if(preview) + { + preview->deleteNotecard(); + } +} + struct LLSaveNotecardInfo { LLPreviewNotecard* mSelf; @@ -466,6 +484,18 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem) return true; } +void LLPreviewNotecard::deleteNotecard() +{ + LLViewerInventoryItem* item = gInventory.getItem(mItemUUID); + if (item != NULL) + { + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + change_item_parent(&gInventory, item, trash_id, FALSE); + } + + closeFloater(); +} + // static void LLPreviewNotecard::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed) { diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index e0363eef54..98de99aa33 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -83,6 +83,8 @@ protected: virtual void loadAsset(); bool saveIfNeeded(LLInventoryItem* copyitem = NULL); + void deleteNotecard(); + static void onLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, @@ -90,6 +92,8 @@ protected: static void onClickSave(void* data); + static void onClickDelete(void* data); + static void onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index af440a3689..de1da248c1 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -706,6 +706,31 @@ void LLScreenChannel::hideToast(const LLUUID& notification_id) } } +void LLScreenChannel::closeHiddenToasts(const Matcher& matcher) +{ + // since we can't guarantee that close toast operation doesn't change mToastList + // we collect matched toasts that should be closed into separate list + std::list<ToastElem> toasts; + for (std::vector<ToastElem>::iterator it = mToastList.begin(); it + != mToastList.end(); it++) + { + LLToast * toast = it->toast; + // add to list valid toast that match to provided matcher criteria + if (toast != NULL && !toast->isDead() && toast->getNotification() != NULL + && !toast->getVisible() && matcher.matches(toast->getNotification())) + { + toasts.push_back(*it); + } + } + + // close collected toasts + for (std::list<ToastElem>::iterator it = toasts.begin(); it + != toasts.end(); it++) + { + it->toast->closeFloater(); + } +} + //-------------------------------------------------------------------------- void LLScreenChannel::removeToastsFromChannel() { diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 88053d87d9..46c5fed7b6 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -173,6 +173,12 @@ public: void hideToastsFromScreen(); // hide toast by notification id void hideToast(const LLUUID& notification_id); + + /** + * Closes hidden matched toasts from channel. + */ + void closeHiddenToasts(const Matcher& matcher); + // removes all toasts from a channel void removeToastsFromChannel(); // show all toasts in a channel diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index fd2bb0fdf9..a4d8dddfe4 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -732,10 +732,17 @@ static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMedi } } +static LLFastTimer::DeclareTimer FTM_MEDIA_UPDATE("Update Media"); + ////////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerMedia::updateMedia(void *dummy_arg) { + LLFastTimer t1(FTM_MEDIA_UPDATE); + + // Enable/disable the plugin read thread + LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread")); + sAnyMediaShowing = false; sUpdatedCookies = getCookieStore()->getChangedCookies(); if(!sUpdatedCookies.empty()) diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index b8fab63be9..56b2791993 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -33,8 +33,11 @@ #include "llwearableitemslist.h" +#include "lliconctrl.h" + #include "llinventoryfunctions.h" #include "llinventorymodel.h" +#include "lltransutil.h" class LLFindOutfitItems : public LLInventoryCollectFunctor { @@ -212,6 +215,87 @@ void LLPanelBodyPartsListItem::setShowEditButton(bool show) ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +LLPanelDummyClothingListItem* LLPanelDummyClothingListItem::create(EWearableType w_type) +{ + LLPanelDummyClothingListItem* list_item = new LLPanelDummyClothingListItem(w_type); + list_item->init(); + return list_item; +} + +void LLPanelDummyClothingListItem::updateItem() +{ + std::string title = wearableTypeToString(mWearableType); + setTitle(title, LLStringUtil::null); +} + +BOOL LLPanelDummyClothingListItem::postBuild() +{ + LLIconCtrl* icon = getChild<LLIconCtrl>("item_icon"); + setIconCtrl(icon); + setTitleCtrl(getChild<LLTextBox>("item_name")); + + addWidgetToRightSide("btn_add"); + + setIconImage(get_item_icon(LLAssetType::AT_CLOTHING, LLInventoryType::IT_NONE, mWearableType, FALSE)); + updateItem(); + + // Make it look loke clothing item - reserve space for 'delete' button + setLeftWidgetsWidth(icon->getRect().mLeft); + + setWidgetsVisible(false); + reshapeWidgets(); + + return TRUE; +} + +LLPanelDummyClothingListItem::LLPanelDummyClothingListItem(EWearableType w_type) + : LLPanelWearableListItem(NULL) + , mWearableType(w_type) +{ +} + +void LLPanelDummyClothingListItem::init() +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_dummy_clothing_list_item.xml"); +} + +typedef std::map<EWearableType, std::string> clothing_to_string_map_t; + +clothing_to_string_map_t init_clothing_string_map() +{ + clothing_to_string_map_t w_map; + w_map.insert(std::make_pair(WT_SHIRT, "shirt_not_worn")); + w_map.insert(std::make_pair(WT_PANTS, "pants_not_worn")); + w_map.insert(std::make_pair(WT_SHOES, "shoes_not_worn")); + w_map.insert(std::make_pair(WT_SOCKS, "socks_not_worn")); + w_map.insert(std::make_pair(WT_JACKET, "jacket_not_worn")); + w_map.insert(std::make_pair(WT_GLOVES, "gloves_not_worn")); + w_map.insert(std::make_pair(WT_UNDERSHIRT, "undershirt_not_worn")); + w_map.insert(std::make_pair(WT_UNDERPANTS, "underpants_not_worn")); + w_map.insert(std::make_pair(WT_SKIRT, "skirt_not_worn")); + w_map.insert(std::make_pair(WT_ALPHA, "alpha_not_worn")); + w_map.insert(std::make_pair(WT_TATTOO, "tattoo_not_worn")); + return w_map; +} + +std::string LLPanelDummyClothingListItem::wearableTypeToString(EWearableType w_type) +{ + static const clothing_to_string_map_t w_map = init_clothing_string_map(); + static const std::string invalid_str = LLTrans::getString("invalid_not_worn"); + + std::string type_str = invalid_str; + clothing_to_string_map_t::const_iterator it = w_map.find(w_type); + if(w_map.end() != it) + { + type_str = LLTrans::getString(it->second); + } + return type_str; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + static const LLDefaultChildRegistry::Register<LLWearableItemsList> r("wearable_items_list"); LLWearableItemsList::Params::Params() diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index ae43b3f673..c4a415dfbf 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -81,7 +81,6 @@ public: virtual ~LLPanelClothingListItem(); - /*virtual*/ void init(); /*virtual*/ BOOL postBuild(); /** @@ -96,6 +95,8 @@ public: protected: LLPanelClothingListItem(LLViewerInventoryItem* item); + + /*virtual*/ void init(); }; class LLPanelBodyPartsListItem : public LLPanelWearableListItem @@ -107,7 +108,6 @@ public: virtual ~LLPanelBodyPartsListItem(); - /*virtual*/ void init(); /*virtual*/ BOOL postBuild(); /** @@ -118,6 +118,32 @@ public: protected: LLPanelBodyPartsListItem(LLViewerInventoryItem* item); + + /*virtual*/ void init(); +}; + +/** + * @class LLPanelDummyClothingListItem + * + * A dummy item panel - displays grayed clothing icon, grayed title '<clothing> not worn' and 'add' button + */ +class LLPanelDummyClothingListItem : public LLPanelWearableListItem +{ +public: + static LLPanelDummyClothingListItem* create(EWearableType w_type); + + /*virtual*/ void updateItem(); + /*virtual*/ BOOL postBuild(); + +protected: + LLPanelDummyClothingListItem(EWearableType w_type); + + /*virtual*/ void init(); + + static std::string wearableTypeToString(EWearableType w_type); + +private: + EWearableType mWearableType; }; /** diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index 14c0081c0d..0e8eef2a21 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -84,8 +84,18 @@ label="Save" label_selected="Save" layout="topleft" - left="288" + left="178" name="Save" top="332" width="100" /> + <button + follows="right|bottom" + height="22" + label="Delete" + label_selected="Delete" + layout="topleft" + left="288" + name="Delete" + top="332" + width="100" /> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 6a5f9ed8f8..19b6b1b22e 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1339,6 +1339,16 @@ function="ToggleControl" parameter="RunMultipleThreads" /> </menu_item_check> + <menu_item_check + label="Use Plugin Read Thread" + name="Use Plugin Read Thread"> + <menu_item_check.on_check + function="CheckControl" + parameter="PluginUseReadThread" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="PluginUseReadThread" /> + </menu_item_check> <menu_item_call label="Clear Group Cache" name="ClearGroupCache"> diff --git a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml index 445f677c94..4313d450fb 100644 --- a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml @@ -48,14 +48,13 @@ top="4" value="..." width="359" /> - <button + <icon name="btn_lock" layout="topleft" follows="top|right" - image_unselected="Lock2" - image_selected="Lock2" + image_name="Lock2" top="0" - left_pad="3" + left="0" height="20" width="20" tab_stop="false" /> diff --git a/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml b/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml index 386b3b2c4f..8dc67de06f 100644 --- a/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml @@ -81,17 +81,15 @@ height="20" width="20" tab_stop="false" /> - <button + <icon name="btn_lock" layout="topleft" follows="top|right" - image_unselected="Lock2" - image_selected="Lock2" + image_name="Lock2" top="0" left_pad="3" height="20" - width="20" - tab_stop="false" /> + width="20" /> <button name="btn_edit" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml b/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml new file mode 100644 index 0000000000..dbbfa8f2e2 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="20" + layout="topleft" + left="0" + name="dummy_clothing_item" + top="0" + width="380"> + <icon + follows="top|right|left" + height="20" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <icon + height="20" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + height="16" + color="0.75 0.75 0.75 1" + follows="top|left" + image_name="Inv_Object" + layout="topleft" + left="20" + name="item_icon" + top="2" + width="16" /> + <text + follows="left|right" + height="16" + layout="topleft" + left_pad="5" + allow_html="false" + use_ellipses="true" + name="item_name" + text_color="LtGray_50" + top="4" + value="..." + width="359" /> + <button + name="btn_add" + layout="topleft" + follows="top|right" + label="+" + top="0" + left="0" + height="20" + width="20" + tab_stop="false" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index 73181392c9..a9f588698a 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -234,6 +234,14 @@ name="move_further_btn" top="1" width="31" /> + <icon + follows="bottom|left" + height="25" + image_name="Toolbar_Middle_Off" + layout="topleft" + left_pad="1" + name="dummy_icon" + width="105" /> <button follows="bottom|right" height="25" @@ -440,6 +448,22 @@ name="add_to_outfit_btn" top="1" width="31" /> + <icon + follows="bottom|left" + height="25" + image_name="Toolbar_Middle_Off" + layout="topleft" + left_pad="1" + name="dummy_middle_icon" + width="140" /> + <icon + follows="bottom|left" + height="25" + image_name="Toolbar_Right_Off" + layout="topleft" + left_pad="1" + name="dummy_right_icon" + width="31" /> </panel> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 066ea3be6e..7e212c9383 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -490,6 +490,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M label="Share" layout="topleft" name="share_btn" + tool_tip="Share an inventory item" width="62" /> <button follows="bottom|left" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index 3ef16d2dec..ba967d3e2c 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -330,7 +330,7 @@ <check_box enabled="false" height="16" - label="Enable plain text chat history" + label="Enable plain text IM and chat history" layout="topleft" left_delta="0" name="plain_text_chat_history" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index f9459bcee2..73df41b776 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -1802,6 +1802,20 @@ Clears (deletes) the media and all params from the given face. <string name="alpha">Alpha</string> <string name="tattoo">Tattoo</string> <string name="invalid">invalid</string> + + <!-- Not Worn Wearable Types --> + <string name="shirt_not_worn">Shirt not worn</string> + <string name="pants_not_worn">Pants not worn</string> + <string name="shoes_not_worn">Shoes not worn</string> + <string name="socks_not_worn">Socks not worn</string> + <string name="jacket_not_worn">Jacket not worn</string> + <string name="gloves_not_worn">Gloves not worn</string> + <string name="undershirt_not_worn">Undershirt not worn</string> + <string name="underpants_not_worn">Underpants not worn</string> + <string name="skirt_not_worn">Skirt not worn</string> + <string name="alpha_not_worn">Alpha not worn</string> + <string name="tattoo_not_worn">Tattoo not worn</string> + <string name="invalid_not_worn">invalid</string> <!-- Wearable List--> <string name="NewWearable">New [WEARABLE_ITEM]</string> |