diff options
Diffstat (limited to 'indra/newview/llcofwearables.cpp')
-rw-r--r-- | indra/newview/llcofwearables.cpp | 330 |
1 files changed, 257 insertions, 73 deletions
diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 7ac3d14c72..04623c03be 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -2,31 +2,25 @@ * @file llcofwearables.cpp * @brief LLCOFWearables displayes wearables from the current outfit split into three lists (attachments, clothing and body parts) * - * $LicenseInfo:firstyear=2010&license=viewergpl$ - * - * Copyright (c) 2010, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code - * 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 + * 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. * - * 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 + * 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. * - * 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. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,6 +28,8 @@ #include "llcofwearables.h" +#include "llaccordionctrl.h" +#include "llaccordionctrltab.h" #include "llagentdata.h" #include "llagentwearables.h" #include "llappearancemgr.h" @@ -58,39 +54,85 @@ static const LLWearableItemNameComparator WEARABLE_NAME_COMPARATOR; class CofContextMenu : public LLListContextMenu { protected: - static void updateCreateWearableLabel(LLMenuGL* menu, const LLUUID& item_id) + CofContextMenu(LLCOFWearables* cof_wearables) + : mCOFWearables(cof_wearables) + { + llassert(mCOFWearables); + } + + void updateCreateWearableLabel(LLMenuGL* menu, const LLUUID& item_id) { LLMenuItemGL* menu_item = menu->getChild<LLMenuItemGL>("create_new"); + LLWearableType::EType w_type = getWearableType(item_id); // Hide the "Create new <WEARABLE_TYPE>" if it's irrelevant. - LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); - if (!item || !item->isWearableType()) + if (w_type == LLWearableType::WT_NONE) { menu_item->setVisible(FALSE); return; } // Set proper label for the "Create new <WEARABLE_TYPE>" menu item. - LLStringUtil::format_map_t args; - LLWearableType::EType w_type = item->getWearableType(); - args["[WEARABLE_TYPE]"] = LLWearableType::getTypeDefaultNewName(w_type); - std::string new_label = LLTrans::getString("CreateNewWearable", args); + std::string new_label = LLTrans::getString("create_new_" + LLWearableType::getTypeName(w_type)); menu_item->setLabel(new_label); } - static void createNew(const LLUUID& item_id) + void createNew(const LLUUID& item_id) { - LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); - if (!item || !item->isWearableType()) return; + LLAgentWearables::createWearable(getWearableType(item_id), true); + } + + // Get wearable type of the given item. + // + // There is a special case: so-called "dummy items" + // (i.e. the ones that are there just to indicate that you're not wearing + // any wearables of the corresponding type. They are currently grayed out + // and suffixed with "not worn"). + // Those items don't have an UUID, but they do have an associated wearable type. + // If the user has invoked context menu for such item, + // we ignore the passed item_id and retrieve wearable type from the item. + LLWearableType::EType getWearableType(const LLUUID& item_id) + { + if (!isDummyItem(item_id)) + { + LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); + if (item && item->isWearableType()) + { + return item->getWearableType(); + } + } + else if (mCOFWearables) // dummy item selected + { + LLPanelDummyClothingListItem* item; - LLAgentWearables::createWearable(item->getWearableType(), true); + item = dynamic_cast<LLPanelDummyClothingListItem*>(mCOFWearables->getSelectedItem()); + if (item) + { + return item->getWearableType(); + } + } + + return LLWearableType::WT_NONE; + } + + static bool isDummyItem(const LLUUID& item_id) + { + return item_id.isNull(); } + + LLCOFWearables* mCOFWearables; }; ////////////////////////////////////////////////////////////////////////// -class CofAttachmentContextMenu : public LLListContextMenu +class CofAttachmentContextMenu : public CofContextMenu { +public: + CofAttachmentContextMenu(LLCOFWearables* cof_wearables) + : CofContextMenu(cof_wearables) + { + } + protected: /*virtual*/ LLContextMenu* createMenu() @@ -108,7 +150,28 @@ protected: class CofClothingContextMenu : public CofContextMenu { +public: + CofClothingContextMenu(LLCOFWearables* cof_wearables) + : CofContextMenu(cof_wearables) + { + } + protected: + static void replaceWearable(const LLUUID& item_id) + { + // *TODO: Most probable that accessing to LLPanelOutfitEdit instance should be: + // LLSideTray::getInstance()->getSidepanelAppearance()->getPanelOutfitEdit() + // without casting. Getter methods provides possibility to check and construct + // absent instance. Explicit relations between components avoids situations + // when we tries to construct instance with unsatisfied implicit input conditions. + LLPanelOutfitEdit * panel_outfit_edit = + dynamic_cast<LLPanelOutfitEdit*> (LLSideTray::getInstance()->getPanel( + "panel_outfit_edit")); + if (panel_outfit_edit != NULL) + { + panel_outfit_edit->onReplaceMenuItemClicked(item_id); + } + } /*virtual*/ LLContextMenu* createMenu() { @@ -118,10 +181,9 @@ protected: functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, mUUIDs)); - registrar.add("Clothing.MoveUp", boost::bind(moveWearable, selected_id, false)); - registrar.add("Clothing.MoveDown", boost::bind(moveWearable, selected_id, true)); + registrar.add("Clothing.Replace", boost::bind(replaceWearable, selected_id)); registrar.add("Clothing.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); - registrar.add("Clothing.Create", boost::bind(createNew, selected_id)); + registrar.add("Clothing.Create", boost::bind(&CofClothingContextMenu::createNew, this, selected_id)); enable_registrar.add("Clothing.OnEnable", boost::bind(&CofClothingContextMenu::onEnable, this, _2)); @@ -139,31 +201,20 @@ protected: std::string param = data.asString(); LLUUID selected_id = mUUIDs.back(); - if ("move_up" == param) - { - return gAgentWearables.canMoveWearable(selected_id, false); - } - else if ("move_down" == param) - { - return gAgentWearables.canMoveWearable(selected_id, true); - } - else if ("take_off" == param) + if ("take_off" == param) { return get_is_item_worn(selected_id); } else if ("edit" == param) { - return gAgentWearables.isWearableModifiable(selected_id); + return mUUIDs.size() == 1 && gAgentWearables.isWearableModifiable(selected_id); + } + else if ("replace" == param) + { + return get_is_item_worn(selected_id) && mUUIDs.size() == 1; } - return true; - } - // We don't use LLAppearanceMgr::moveWearable() directly because - // the item may be invalidated between setting the callback and calling it. - static bool moveWearable(const LLUUID& item_id, bool closer_to_body) - { - LLViewerInventoryItem* item = gInventory.getItem(item_id); - return LLAppearanceMgr::instance().moveWearable(item, closer_to_body); + return true; } }; @@ -171,8 +222,13 @@ protected: class CofBodyPartContextMenu : public CofContextMenu { -protected: +public: + CofBodyPartContextMenu(LLCOFWearables* cof_wearables) + : CofContextMenu(cof_wearables) + { + } +protected: /*virtual*/ LLContextMenu* createMenu() { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; @@ -182,9 +238,9 @@ protected: // *HACK* need to pass pointer to LLPanelOutfitEdit instead of LLSideTray::getInstance()->getPanel(). // LLSideTray::getInstance()->getPanel() is rather slow variant LLPanelOutfitEdit* panel_oe = dynamic_cast<LLPanelOutfitEdit*>(LLSideTray::getInstance()->getPanel("panel_outfit_edit")); - registrar.add("BodyPart.Replace", boost::bind(&LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked, panel_oe, selected_id)); + registrar.add("BodyPart.Replace", boost::bind(&LLPanelOutfitEdit::onReplaceMenuItemClicked, panel_oe, selected_id)); registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); - registrar.add("BodyPart.Create", boost::bind(createNew, selected_id)); + registrar.add("BodyPart.Create", boost::bind(&CofBodyPartContextMenu::createNew, this, selected_id)); enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2)); @@ -204,7 +260,7 @@ protected: if ("edit" == param) { - return gAgentWearables.isWearableModifiable(selected_id); + return mUUIDs.size() == 1 && gAgentWearables.isWearableModifiable(selected_id); } return true; @@ -217,11 +273,17 @@ LLCOFWearables::LLCOFWearables() : LLPanel(), mAttachments(NULL), mClothing(NULL), mBodyParts(NULL), - mLastSelectedList(NULL) + mLastSelectedList(NULL), + mClothingTab(NULL), + mAttachmentsTab(NULL), + mBodyPartsTab(NULL), + mLastSelectedTab(NULL), + mAccordionCtrl(NULL), + mCOFVersion(-1) { - mClothingMenu = new CofClothingContextMenu(); - mAttachmentMenu = new CofAttachmentContextMenu(); - mBodyPartMenu = new CofBodyPartContextMenu(); + mClothingMenu = new CofClothingContextMenu(this); + mAttachmentMenu = new CofAttachmentContextMenu(this); + mBodyPartMenu = new CofBodyPartContextMenu(this); }; LLCOFWearables::~LLCOFWearables() @@ -255,6 +317,22 @@ BOOL LLCOFWearables::postBuild() mAttachments->setComparator(&WEARABLE_NAME_COMPARATOR); mBodyParts->setComparator(&WEARABLE_NAME_COMPARATOR); + + mClothingTab = getChild<LLAccordionCtrlTab>("tab_clothing"); + mClothingTab->setDropDownStateChangedCallback(boost::bind(&LLCOFWearables::onAccordionTabStateChanged, this, _1, _2)); + + mAttachmentsTab = getChild<LLAccordionCtrlTab>("tab_attachments"); + mAttachmentsTab->setDropDownStateChangedCallback(boost::bind(&LLCOFWearables::onAccordionTabStateChanged, this, _1, _2)); + + mBodyPartsTab = getChild<LLAccordionCtrlTab>("tab_body_parts"); + mBodyPartsTab->setDropDownStateChangedCallback(boost::bind(&LLCOFWearables::onAccordionTabStateChanged, this, _1, _2)); + + mTab2AssetType[mClothingTab] = LLAssetType::AT_CLOTHING; + mTab2AssetType[mAttachmentsTab] = LLAssetType::AT_OBJECT; + mTab2AssetType[mBodyPartsTab] = LLAssetType::AT_BODYPART; + + mAccordionCtrl = getChild<LLAccordionCtrl>("cof_wearables_accordion"); + return LLPanel::postBuild(); } @@ -274,8 +352,51 @@ void LLCOFWearables::onSelectionChange(LLFlatListView* selected_list) onCommit(); } +void LLCOFWearables::onAccordionTabStateChanged(LLUICtrl* ctrl, const LLSD& expanded) +{ + bool had_selected_items = mClothing->numSelected() || mAttachments->numSelected() || mBodyParts->numSelected(); + mClothing->resetSelection(true); + mAttachments->resetSelection(true); + mBodyParts->resetSelection(true); + + bool tab_selection_changed = false; + LLAccordionCtrlTab* tab = dynamic_cast<LLAccordionCtrlTab*>(ctrl); + if (tab && tab != mLastSelectedTab) + { + mLastSelectedTab = tab; + tab_selection_changed = true; + } + + if (had_selected_items || tab_selection_changed) + { + //sending commit signal to indicate selection changes + onCommit(); + } +} + void LLCOFWearables::refresh() { + const LLUUID cof_id = LLAppearanceMgr::instance().getCOF(); + if (cof_id.isNull()) + { + llwarns << "COF ID cannot be NULL" << llendl; + return; + } + + LLViewerInventoryCategory* catp = gInventory.getCategory(cof_id); + if (!catp) + { + llwarns << "COF category cannot be NULL" << llendl; + return; + } + + // BAP - this check has to be removed because an item name change does not + // change cat version - ie, checking version is not a complete way + // of finding out whether anything has changed in this category. + //if (mCOFVersion == catp->getVersion()) return; + + mCOFVersion = catp->getVersion(); + typedef std::vector<LLSD> values_vector_t; typedef std::map<LLFlatListView*, values_vector_t> selection_map_t; @@ -291,7 +412,7 @@ void LLCOFWearables::refresh() LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t cof_items; - gInventory.collectDescendents(LLAppearanceMgr::getInstance()->getCOF(), cats, cof_items, LLInventoryModel::EXCLUDE_TRASH); + gInventory.collectDescendents(cof_id, cats, cof_items, LLInventoryModel::EXCLUDE_TRASH); populateAttachmentsAndBodypartsLists(cof_items); @@ -308,14 +429,26 @@ void LLCOFWearables::refresh() iter != iter_end; ++iter) { LLFlatListView* list = iter->first; + if (!list) continue; + + //restoring selection should not fire commit callbacks + list->setCommitOnSelectionChange(false); + const values_vector_t& values = iter->second; for (values_vector_t::const_iterator value_it = values.begin(), value_it_end = values.end(); value_it != value_it_end; ++value_it) { - list->selectItemByValue(*value_it); + // value_it may be null because of dummy items + // Dummy items have no ID + if(value_it->asUUID().notNull()) + { + list->selectItemByValue(*value_it); + } } + + list->setCommitOnSelectionChange(true); } } @@ -387,13 +520,7 @@ LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventory item_panel->childSetAction("btn_edit", mCOFCallbacks.mEditWearable); //turning on gray separator line for the last item in the items group of the same wearable type - if (last) - { - LLRect rect = item_panel->getRect(); - item_panel->reshape(rect.getWidth(), rect.getHeight() + - item_panel->getChild<LLView>("wearable_type_separator_icon")->getRect().getHeight()); - item_panel->childSetVisible("wearable_type_separator_icon", true); - } + item_panel->setSeparatorVisible(last); return item_panel; } @@ -427,7 +554,7 @@ LLPanelDeletableWearableListItem* LLCOFWearables::buildAttachemntListItem(LLView llassert(item); if (!item) return NULL; - LLPanelDeletableWearableListItem* item_panel = LLPanelDeletableWearableListItem::create(item); + LLPanelAttachmentListItem* item_panel = LLPanelAttachmentListItem::create(item); if (!item_panel) return NULL; //setting callbacks @@ -505,6 +632,14 @@ LLPanel* LLCOFWearables::getSelectedItem() return mLastSelectedList->getSelectedItem(); } +void LLCOFWearables::getSelectedItems(std::vector<LLPanel*>& selected_items) const +{ + if (mLastSelectedList) + { + mLastSelectedList->getSelectedItems(selected_items); + } +} + void LLCOFWearables::clear() { mAttachments->clear(); @@ -512,6 +647,42 @@ void LLCOFWearables::clear() mBodyParts->clear(); } +LLAssetType::EType LLCOFWearables::getExpandedAccordionAssetType() +{ + typedef std::map<std::string, LLAssetType::EType> type_map_t; + + static type_map_t type_map; + + if (mAccordionCtrl != NULL) + { + const LLAccordionCtrlTab* expanded_tab = mAccordionCtrl->getExpandedTab(); + + return get_if_there(mTab2AssetType, expanded_tab, LLAssetType::AT_NONE); + } + + return LLAssetType::AT_NONE; +} + +LLAssetType::EType LLCOFWearables::getSelectedAccordionAssetType() +{ + if (mAccordionCtrl != NULL) + { + const LLAccordionCtrlTab* selected_tab = mAccordionCtrl->getSelectedTab(); + + return get_if_there(mTab2AssetType, selected_tab, LLAssetType::AT_NONE); + } + + return LLAssetType::AT_NONE; +} + +void LLCOFWearables::expandDefaultAccordionTab() +{ + if (mAccordionCtrl != NULL) + { + mAccordionCtrl->expandDefaultTab(); + } +} + void LLCOFWearables::onListRightClick(LLUICtrl* ctrl, S32 x, S32 y, LLListContextMenu* menu) { if(menu) @@ -519,7 +690,20 @@ void LLCOFWearables::onListRightClick(LLUICtrl* ctrl, S32 x, S32 y, LLListContex uuid_vec_t selected_uuids; if(getSelectedUUIDs(selected_uuids)) { - menu->show(ctrl, selected_uuids, x, y); + bool show_menu = false; + for(uuid_vec_t::iterator it = selected_uuids.begin();it!=selected_uuids.end();++it) + { + if ((*it).notNull()) + { + show_menu = true; + break; + } + } + + if(show_menu) + { + menu->show(ctrl, selected_uuids, x, y); + } } } } |