diff options
Diffstat (limited to 'indra/newview/llpaneloutfitedit.cpp')
-rw-r--r-- | indra/newview/llpaneloutfitedit.cpp | 1415 |
1 files changed, 1136 insertions, 279 deletions
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index ce17e1d624..494db01f77 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -2,31 +2,25 @@ * @file llpaneloutfitedit.cpp * @brief Displays outfit edit information in Side Tray. * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2009&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. * - * 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 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,9 +30,15 @@ // *TODO: reorder includes to match the coding standard #include "llagent.h" +#include "llagentcamera.h" #include "llagentwearables.h" #include "llappearancemgr.h" +#include "lloutfitobserver.h" +#include "llcofwearables.h" +#include "llfilteredwearablelist.h" +#include "llfolderviewitem.h" #include "llinventory.h" +#include "llinventoryitemslist.h" #include "llviewercontrol.h" #include "llui.h" #include "llfloater.h" @@ -55,15 +55,22 @@ #include "llinventorybridge.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" +#include "llloadingindicator.h" #include "llpaneloutfitsinventory.h" #include "lluiconstants.h" +#include "llsaveoutfitcombobtn.h" #include "llscrolllistctrl.h" #include "lltextbox.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llsdutil.h" #include "llsidepanelappearance.h" #include "lltoggleablemenu.h" +#include "llvoavatarself.h" #include "llwearablelist.h" +#include "llwearableitemslist.h" +#include "llwearabletype.h" +#include "llweb.h" static LLRegisterPanelClassWrapper<LLPanelOutfitEdit> t_outfit_edit("panel_outfit_edit"); @@ -71,212 +78,607 @@ const U64 WEARABLE_MASK = (1LL << LLInventoryType::IT_WEARABLE); const U64 ATTACHMENT_MASK = (1LL << LLInventoryType::IT_ATTACHMENT) | (1LL << LLInventoryType::IT_OBJECT); const U64 ALL_ITEMS_MASK = WEARABLE_MASK | ATTACHMENT_MASK; -class LLInventoryLookObserver : public LLInventoryObserver +static const std::string REVERT_BTN("revert_btn"); + + +/////////////////////////////////////////////////////////////////////////////// +// LLShopURLDispatcher +/////////////////////////////////////////////////////////////////////////////// + +class LLShopURLDispatcher { public: - LLInventoryLookObserver(LLPanelOutfitEdit *panel) : mPanel(panel) {} - virtual ~LLInventoryLookObserver() + std::string resolveURL(LLWearableType::EType wearable_type, ESex sex); + std::string resolveURL(LLAssetType::EType asset_type, ESex sex); +}; + +std::string LLShopURLDispatcher::resolveURL(LLWearableType::EType wearable_type, ESex sex) +{ + const std::string prefix = "MarketplaceURL"; + const std::string sex_str = (sex == SEX_MALE) ? "Male" : "Female"; + const std::string type_str = LLWearableType::getTypeName(wearable_type); + + std::string setting_name = prefix; + + switch (wearable_type) + { + case LLWearableType::WT_ALPHA: + case LLWearableType::WT_NONE: + case LLWearableType::WT_INVALID: // just in case, this shouldn't happen + case LLWearableType::WT_COUNT: // just in case, this shouldn't happen + break; + + default: + setting_name += '_'; + setting_name += type_str; + setting_name += sex_str; + break; + } + + return gSavedSettings.getString(setting_name); +} + +std::string LLShopURLDispatcher::resolveURL(LLAssetType::EType asset_type, ESex sex) +{ + const std::string prefix = "MarketplaceURL"; + const std::string sex_str = (sex == SEX_MALE) ? "Male" : "Female"; + const std::string type_str = LLAssetType::lookup(asset_type); + + std::string setting_name = prefix; + + switch (asset_type) { - if (gInventory.containsObserver(this)) + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_OBJECT: + case LLAssetType::AT_BODYPART: + setting_name += '_'; + setting_name += type_str; + setting_name += sex_str; + break; + + // to suppress warnings + default: + break; + } + + return gSavedSettings.getString(setting_name); +} + +/////////////////////////////////////////////////////////////////////////////// +// LLPanelOutfitEditGearMenu +/////////////////////////////////////////////////////////////////////////////// + +class LLPanelOutfitEditGearMenu +{ +public: + static LLMenuGL* create() + { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + registrar.add("Wearable.Create", boost::bind(onCreate, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( + "menu_cof_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); + llassert(menu); + if (menu) { - gInventory.removeObserver(this); + populateCreateWearableSubmenus(menu); } + + return menu; } - - virtual void changed(U32 mask) + +private: + static void onCreate(const LLSD& param) { - if (mask & (LLInventoryObserver::ADD | LLInventoryObserver::REMOVE)) + LLWearableType::EType type = LLWearableType::typeNameToType(param.asString()); + if (type == LLWearableType::WT_NONE) { - mPanel->updateLookInfo(); + llwarns << "Invalid wearable type" << llendl; + return; + } + + LLAgentWearables::createWearable(type, true); + } + + // Populate the menu with items like "New Skin", "New Pants", etc. + static void populateCreateWearableSubmenus(LLMenuGL* menu) + { + LLView* menu_clothes = gMenuHolder->findChildView("COF.Gear.New_Clothes", FALSE); + LLView* menu_bp = gMenuHolder->findChildView("COF.Geear.New_Body_Parts", FALSE); + + if (!menu_clothes || !menu_bp) + { + llassert(menu_clothes && menu_bp); + return; + } + + for (U8 i = LLWearableType::WT_SHAPE; i != (U8) LLWearableType::WT_COUNT; ++i) + { + LLWearableType::EType type = (LLWearableType::EType) i; + const std::string& type_name = LLWearableType::getTypeName(type); + + LLMenuItemCallGL::Params p; + p.name = type_name; + p.label = LLTrans::getString(LLWearableType::getTypeDefaultNewName(type)); + p.on_click.function_name = "Wearable.Create"; + p.on_click.parameter = LLSD(type_name); + + LLView* parent = LLWearableType::getAssetType(type) == LLAssetType::AT_CLOTHING ? + menu_clothes : menu_bp; + LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent); } } -protected: - LLPanelOutfitEdit *mPanel; }; -class LLLookFetchObserver : public LLInventoryFetchDescendentsObserver +/////////////////////////////////////////////////////////////////////////////// +// LLAddWearablesGearMenu +/////////////////////////////////////////////////////////////////////////////// + +class LLAddWearablesGearMenu : public LLInitClass<LLAddWearablesGearMenu> { public: - LLLookFetchObserver(LLPanelOutfitEdit *panel) : - mPanel(panel) - {} - LLLookFetchObserver() {} - virtual void done() + static LLMenuGL* create(LLWearableItemsList* flat_list, LLInventoryPanel* inventory_panel) { - mPanel->lookFetched(); - if(gInventory.containsObserver(this)) + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + llassert(flat_list); + llassert(inventory_panel); + + LLHandle<LLView> flat_list_handle = flat_list->getHandle(); + LLHandle<LLPanel> inventory_panel_handle = inventory_panel->getHandle(); + + registrar.add("AddWearable.Gear.Sort", boost::bind(onSort, flat_list_handle, inventory_panel_handle, _2)); + enable_registrar.add("AddWearable.Gear.Check", boost::bind(onCheck, flat_list_handle, inventory_panel_handle, _2)); + enable_registrar.add("AddWearable.Gear.Visible", boost::bind(onVisible, inventory_panel_handle, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( + "menu_add_wearable_gear.xml", + LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); + + return menu; + } + +private: + static void onSort(LLHandle<LLView> flat_list_handle, + LLHandle<LLPanel> inventory_panel_handle, + LLSD::String sort_order_str) + { + if (flat_list_handle.isDead() || inventory_panel_handle.isDead()) return; + + LLWearableItemsList* flat_list = dynamic_cast<LLWearableItemsList*>(flat_list_handle.get()); + LLInventoryPanel* inventory_panel = dynamic_cast<LLInventoryPanel*>(inventory_panel_handle.get()); + + if (!flat_list || !inventory_panel) return; + + LLWearableItemsList::ESortOrder sort_order; + + if ("by_most_recent" == sort_order_str) + { + sort_order = LLWearableItemsList::E_SORT_BY_MOST_RECENT; + } + else if ("by_name" == sort_order_str) + { + sort_order = LLWearableItemsList::E_SORT_BY_NAME; + } + else if ("by_type" == sort_order_str) + { + sort_order = LLWearableItemsList::E_SORT_BY_TYPE_NAME; + } + else + { + llwarns << "Unrecognized sort order action" << llendl; + return; + } + + if (inventory_panel->getVisible()) + { + inventory_panel->setSortOrder(sort_order); + } + else + { + flat_list->setSortOrder(sort_order); + } + } + + static bool onCheck(LLHandle<LLView> flat_list_handle, + LLHandle<LLPanel> inventory_panel_handle, + LLSD::String sort_order_str) + { + if (flat_list_handle.isDead() || inventory_panel_handle.isDead()) return false; + + LLWearableItemsList* flat_list = dynamic_cast<LLWearableItemsList*>(flat_list_handle.get()); + LLInventoryPanel* inventory_panel = dynamic_cast<LLInventoryPanel*>(inventory_panel_handle.get()); + + if (!inventory_panel || !flat_list) return false; + + // Inventory panel uses its own sort order independent from + // flat list view so this flag is used to distinguish between + // currently visible "tree" or "flat" representation of inventory. + bool inventory_tree_visible = inventory_panel->getVisible(); + + if (inventory_tree_visible) { - gInventory.removeObserver(this); + U32 sort_order = inventory_panel->getSortOrder(); + + if ("by_most_recent" == sort_order_str) + { + return LLWearableItemsList::E_SORT_BY_MOST_RECENT & sort_order; + } + else if ("by_name" == sort_order_str) + { + // If inventory panel is not sorted by date then it is sorted by name. + return LLWearableItemsList::E_SORT_BY_MOST_RECENT & ~sort_order; + } + llwarns << "Unrecognized inventory panel sort order" << llendl; } + else + { + LLWearableItemsList::ESortOrder sort_order = flat_list->getSortOrder(); + + if ("by_most_recent" == sort_order_str) + { + return LLWearableItemsList::E_SORT_BY_MOST_RECENT == sort_order; + } + else if ("by_name" == sort_order_str) + { + return LLWearableItemsList::E_SORT_BY_NAME == sort_order; + } + else if ("by_type" == sort_order_str) + { + return LLWearableItemsList::E_SORT_BY_TYPE_NAME == sort_order; + } + llwarns << "Unrecognized wearable list sort order" << llendl; + } + return false; + } + + static bool onVisible(LLHandle<LLPanel> inventory_panel_handle, + LLSD::String sort_order_str) + { + if (inventory_panel_handle.isDead()) return false; + + LLInventoryPanel* inventory_panel = dynamic_cast<LLInventoryPanel*>(inventory_panel_handle.get()); + + // Enable sorting by type only for the flat list of items + // because inventory panel doesn't support this kind of sorting. + return ( "by_type" == sort_order_str ) + && ( !inventory_panel || !inventory_panel->getVisible() ); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// LLCOFDragAndDropObserver +/////////////////////////////////////////////////////////////////////////////// + +class LLCOFDragAndDropObserver : public LLInventoryAddItemByAssetObserver +{ +public: + LLCOFDragAndDropObserver(LLInventoryModel* model); + + virtual ~LLCOFDragAndDropObserver(); + + virtual void done(); + private: - LLPanelOutfitEdit *mPanel; + LLInventoryModel* mModel; }; +inline LLCOFDragAndDropObserver::LLCOFDragAndDropObserver(LLInventoryModel* model): + mModel(model) +{ + if (model != NULL) + { + model->addObserver(this); + } +} +inline LLCOFDragAndDropObserver::~LLCOFDragAndDropObserver() +{ + if (mModel != NULL && mModel->containsObserver(this)) + { + mModel->removeObserver(this); + } +} + +void LLCOFDragAndDropObserver::done() +{ + LLAppearanceMgr::instance().updateAppearanceFromCOF(); +} + +/////////////////////////////////////////////////////////////////////////////// +// LLPanelOutfitEdit +/////////////////////////////////////////////////////////////////////////////// LLPanelOutfitEdit::LLPanelOutfitEdit() -: LLPanel(), mCurrentOutfitID(), mFetchLook(NULL), mSearchFilter(NULL), -mLookContents(NULL), mInventoryItemsPanel(NULL), mAddToOutfitBtn(NULL), -mRemoveFromOutfitBtn(NULL), mLookObserver(NULL) +: LLPanel(), + mSearchFilter(NULL), + mCOFWearables(NULL), + mInventoryItemsPanel(NULL), + mGearMenu(NULL), + mAddWearablesGearMenu(NULL), + mCOFDragAndDropObserver(NULL), + mInitialized(false), + mAddWearablesPanel(NULL), + mFolderViewFilterCmbBox(NULL), + mListViewFilterCmbBox(NULL), + mPlusBtn(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); - mFetchLook = new LLLookFetchObserver(this); - mLookObserver = new LLInventoryLookObserver(this); - gInventory.addObserver(mLookObserver); + + LLOutfitObserver& observer = LLOutfitObserver::instance(); + observer.addBOFReplacedCallback(boost::bind(&LLPanelOutfitEdit::updateCurrentOutfitName, this)); + observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this)); + observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this)); + observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitEdit::onCOFChanged, this)); + + gAgentWearables.addLoadingStartedCallback(boost::bind(&LLPanelOutfitEdit::onOutfitChanging, this, true)); + gAgentWearables.addLoadedCallback(boost::bind(&LLPanelOutfitEdit::onOutfitChanging, this, false)); - mLookItemTypes.reserve(NUM_LOOK_ITEM_TYPES); - for (U32 i = 0; i < NUM_LOOK_ITEM_TYPES; i++) + mFolderViewItemTypes.reserve(NUM_FOLDER_VIEW_ITEM_TYPES); + for (U32 i = 0; i < NUM_FOLDER_VIEW_ITEM_TYPES; i++) { - mLookItemTypes.push_back(LLLookItemType()); + mFolderViewItemTypes.push_back(LLLookItemType()); } - } LLPanelOutfitEdit::~LLPanelOutfitEdit() { delete mSavedFolderState; - if (gInventory.containsObserver(mFetchLook)) - { - gInventory.removeObserver(mFetchLook); - } - delete mFetchLook; - - if (gInventory.containsObserver(mLookObserver)) - { - gInventory.removeObserver(mLookObserver); + + delete mCOFDragAndDropObserver; + + while (!mListViewItemTypes.empty()) { + delete mListViewItemTypes.back(); + mListViewItemTypes.pop_back(); } - delete mLookObserver; } BOOL LLPanelOutfitEdit::postBuild() { // gInventory.isInventoryUsable() no longer needs to be tested per Richard's fix for race conditions between inventory and panels - - mLookItemTypes[LIT_ALL] = LLLookItemType(getString("Filter.All"), ALL_ITEMS_MASK); - mLookItemTypes[LIT_WEARABLE] = LLLookItemType(getString("Filter.Clothes/Body"), WEARABLE_MASK); - mLookItemTypes[LIT_ATTACHMENT] = LLLookItemType(getString("Filter.Objects"), ATTACHMENT_MASK); + + mFolderViewItemTypes[FVIT_ALL] = LLLookItemType(getString("Filter.All"), ALL_ITEMS_MASK); + mFolderViewItemTypes[FVIT_WEARABLE] = LLLookItemType(getString("Filter.Clothes/Body"), WEARABLE_MASK); + mFolderViewItemTypes[FVIT_ATTACHMENT] = LLLookItemType(getString("Filter.Objects"), ATTACHMENT_MASK); + + //order is important, see EListViewItemType for order information + mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.All"), new LLFindNonLinksByMask(ALL_ITEMS_MASK))); + mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.Clothing"), new LLIsTypeActual(LLAssetType::AT_CLOTHING))); + mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.Bodyparts"), new LLIsTypeActual(LLAssetType::AT_BODYPART))); + mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.Objects"), new LLFindNonLinksByMask(ATTACHMENT_MASK)));; + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("shape"), new LLFindActualWearablesOfType(LLWearableType::WT_SHAPE))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("skin"), new LLFindActualWearablesOfType(LLWearableType::WT_SKIN))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("hair"), new LLFindActualWearablesOfType(LLWearableType::WT_HAIR))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("eyes"), new LLFindActualWearablesOfType(LLWearableType::WT_EYES))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("shirt"), new LLFindActualWearablesOfType(LLWearableType::WT_SHIRT))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("pants"), new LLFindActualWearablesOfType(LLWearableType::WT_PANTS))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("shoes"), new LLFindActualWearablesOfType(LLWearableType::WT_SHOES))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("socks"), new LLFindActualWearablesOfType(LLWearableType::WT_SOCKS))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("jacket"), new LLFindActualWearablesOfType(LLWearableType::WT_JACKET))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("gloves"), new LLFindActualWearablesOfType(LLWearableType::WT_GLOVES))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("undershirt"), new LLFindActualWearablesOfType(LLWearableType::WT_UNDERSHIRT))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("underpants"), new LLFindActualWearablesOfType(LLWearableType::WT_UNDERPANTS))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("skirt"), new LLFindActualWearablesOfType(LLWearableType::WT_SKIRT))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("alpha"), new LLFindActualWearablesOfType(LLWearableType::WT_ALPHA))); + mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("tattoo"), new LLFindActualWearablesOfType(LLWearableType::WT_TATTOO))); mCurrentOutfitName = getChild<LLTextBox>("curr_outfit_name"); + mStatus = getChild<LLTextBox>("status"); + + mFolderViewBtn = getChild<LLButton>("folder_view_btn"); + mListViewBtn = getChild<LLButton>("list_view_btn"); - childSetCommitCallback("add_btn", boost::bind(&LLPanelOutfitEdit::showAddWearablesPanel, this), NULL); childSetCommitCallback("filter_button", boost::bind(&LLPanelOutfitEdit::showWearablesFilter, this), NULL); + childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::showWearablesFolderView, this), NULL); + childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::saveListSelection, this), NULL); + childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::showWearablesListView, this), NULL); + childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::saveListSelection, this), NULL); + childSetCommitCallback("wearables_gear_menu_btn", boost::bind(&LLPanelOutfitEdit::onGearButtonClick, this, _1), NULL); + childSetCommitCallback("gear_menu_btn", boost::bind(&LLPanelOutfitEdit::onGearButtonClick, this, _1), NULL); + childSetCommitCallback("shop_btn_1", boost::bind(&LLPanelOutfitEdit::onShopButtonClicked, this), NULL); + childSetCommitCallback("shop_btn_2", boost::bind(&LLPanelOutfitEdit::onShopButtonClicked, this), NULL); + + setVisibleCallback(boost::bind(&LLPanelOutfitEdit::onVisibilityChange, this, _2)); - mLookContents = getChild<LLScrollListCtrl>("look_items_list"); - mLookContents->sortByColumn("look_item_sort", TRUE); - mLookContents->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onOutfitItemSelectionChange, this)); + mCOFWearables = findChild<LLCOFWearables>("cof_wearables_list"); + mCOFWearables->setCommitCallback(boost::bind(&LLPanelOutfitEdit::filterWearablesBySelectedItem, this)); - mInventoryItemsPanel = getChild<LLInventoryPanel>("inventory_items"); + mCOFWearables->getCOFCallbacks().mAddWearable = boost::bind(&LLPanelOutfitEdit::onAddWearableClicked, this); + mCOFWearables->getCOFCallbacks().mEditWearable = boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this); + mCOFWearables->getCOFCallbacks().mDeleteWearable = boost::bind(&LLPanelOutfitEdit::onRemoveFromOutfitClicked, this); + mCOFWearables->getCOFCallbacks().mMoveWearableCloser = boost::bind(&LLPanelOutfitEdit::moveWearable, this, true); + mCOFWearables->getCOFCallbacks().mMoveWearableFurther = boost::bind(&LLPanelOutfitEdit::moveWearable, this, false); + + mAddWearablesPanel = getChild<LLPanel>("add_wearables_panel"); + + mInventoryItemsPanel = getChild<LLInventoryPanel>("folder_view"); mInventoryItemsPanel->setFilterTypes(ALL_ITEMS_MASK); mInventoryItemsPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mInventoryItemsPanel->setSelectCallback(boost::bind(&LLPanelOutfitEdit::onInventorySelectionChange, this, _1, _2)); - mInventoryItemsPanel->getRootFolder()->setReshapeCallback(boost::bind(&LLPanelOutfitEdit::onInventorySelectionChange, this, _1, _2)); - - LLComboBox* type_filter = getChild<LLComboBox>("filter_wearables_combobox"); - type_filter->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onTypeFilterChanged, this, _1)); - type_filter->removeall(); - for (U32 i = 0; i < mLookItemTypes.size(); ++i) + mInventoryItemsPanel->setSelectCallback(boost::bind(&LLPanelOutfitEdit::updatePlusButton, this)); + mInventoryItemsPanel->getRootFolder()->setReshapeCallback(boost::bind(&LLPanelOutfitEdit::updatePlusButton, this)); + + mCOFDragAndDropObserver = new LLCOFDragAndDropObserver(mInventoryItemsPanel->getModel()); + + mFolderViewFilterCmbBox = getChild<LLComboBox>("folder_view_filter_combobox"); + mFolderViewFilterCmbBox->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onFolderViewFilterCommitted, this, _1)); + mFolderViewFilterCmbBox->removeall(); + for (U32 i = 0; i < mFolderViewItemTypes.size(); ++i) { - type_filter->add(mLookItemTypes[i].displayName); + mFolderViewFilterCmbBox->add(mFolderViewItemTypes[i].displayName); } - type_filter->setCurrentByIndex(LIT_ALL); + mFolderViewFilterCmbBox->setCurrentByIndex(FVIT_ALL); + mListViewFilterCmbBox = getChild<LLComboBox>("list_view_filter_combobox"); + mListViewFilterCmbBox->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onListViewFilterCommitted, this, _1)); + mListViewFilterCmbBox->removeall(); + for (U32 i = 0; i < mListViewItemTypes.size(); ++i) + { + mListViewFilterCmbBox->add(mListViewItemTypes[i]->displayName); + } + mListViewFilterCmbBox->setCurrentByIndex(LVIT_ALL); + mSearchFilter = getChild<LLFilterEditor>("look_item_filter"); mSearchFilter->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onSearchEdit, this, _2)); - - /* Removing add to look inline button (not part of mvp for viewer 2) - LLButton::Params add_params; - add_params.name("add_to_look"); - add_params.click_callback.function(boost::bind(&LLPanelOutfitEdit::onAddToLookClicked, this)); - add_params.label("+"); - - mAddToLookBtn = LLUICtrlFactory::create<LLButton>(add_params); - mAddToLookBtn->setEnabled(FALSE); - mAddToLookBtn->setVisible(FALSE); */ - - childSetAction("add_to_outfit_btn", boost::bind(&LLPanelOutfitEdit::onAddToOutfitClicked, this)); - childSetEnabled("add_to_outfit_btn", false); - - mUpBtn = getChild<LLButton>("up_btn"); - mUpBtn->setEnabled(TRUE); - mUpBtn->setClickedCallback(boost::bind(&LLPanelOutfitEdit::onUpClicked, this)); - - //*TODO rename mLookContents to mOutfitContents - mLookContents = getChild<LLScrollListCtrl>("look_items_list"); - mLookContents->sortByColumn("look_item_sort", TRUE); - mLookContents->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onOutfitItemSelectionChange, this)); - mRemoveFromOutfitBtn = getChild<LLButton>("remove_from_outfit_btn"); - mRemoveFromOutfitBtn->setEnabled(FALSE); - mRemoveFromOutfitBtn->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onRemoveFromOutfitClicked, this)); + childSetAction("show_add_wearables_btn", boost::bind(&LLPanelOutfitEdit::onAddMoreButtonClicked, this)); + mPlusBtn = getChild<LLButton>("plus_btn"); + mPlusBtn->setClickedCallback(boost::bind(&LLPanelOutfitEdit::onPlusBtnClicked, this)); + mEditWearableBtn = getChild<LLButton>("edit_wearable_btn"); mEditWearableBtn->setEnabled(FALSE); mEditWearableBtn->setVisible(FALSE); mEditWearableBtn->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this)); - childSetAction("revert_btn", boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); + childSetAction(REVERT_BTN, boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); + + /* + * By default AT_CLOTHING are sorted by (in in MY OUTFITS): + * - by type (types order determined in LLWearableType::EType) + * - each LLWearableType::EType by outer layer on top + * + * In Add More panel AT_CLOTHING should be sorted in a such way: + * - by type (types order determined in LLWearableType::EType) + * - each LLWearableType::EType by name (EXT-8205) + */ + mWearableListViewItemsComparator = new LLWearableItemTypeNameComparator(); + mWearableListViewItemsComparator->setOrder(LLAssetType::AT_CLOTHING, LLWearableItemTypeNameComparator::ORDER_RANK_1, false, true); + + mWearablesListViewPanel = getChild<LLPanel>("filtered_wearables_panel"); + mWearableItemsList = getChild<LLWearableItemsList>("list_view"); + mWearableItemsList->setCommitOnSelectionChange(true); + mWearableItemsList->setCommitCallback(boost::bind(&LLPanelOutfitEdit::updatePlusButton, this)); + mWearableItemsList->setDoubleClickCallback(boost::bind(&LLPanelOutfitEdit::onPlusBtnClicked, this)); + + mWearableItemsList->setComparator(mWearableListViewItemsComparator); + + mSaveComboBtn.reset(new LLSaveOutfitComboBtn(this)); + return TRUE; +} - childSetAction("save_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false)); - childSetAction("save_as_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); - childSetAction("save_flyout_btn", boost::bind(&LLPanelOutfitEdit::showSaveMenu, this)); +// virtual +void LLPanelOutfitEdit::onOpen(const LLSD& key) +{ + if (!mInitialized) + { + // *TODO: this method is called even panel is not visible to user because its parent layout panel is hidden. + // So, we can defer initializing a bit. + mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, mListViewItemTypes[LVIT_ALL]->collector); + mWearableListManager->populateList(); + displayCurrentOutfit(); + mInitialized = true; + } +} - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar save_registar; - save_registar.add("Outfit.Save.Action", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false)); - save_registar.add("Outfit.SaveAsNew.Action", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); - mSaveMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_save_outfit.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +void LLPanelOutfitEdit::moveWearable(bool closer_to_body) +{ + LLUUID item_id = mCOFWearables->getSelectedUUID(); + if (item_id.isNull()) return; + + LLViewerInventoryItem* wearable_to_move = gInventory.getItem(item_id); + LLAppearanceMgr::getInstance()->moveWearable(wearable_to_move, closer_to_body); +} - return TRUE; +void LLPanelOutfitEdit::toggleAddWearablesPanel() +{ + BOOL current_visibility = mAddWearablesPanel->getVisible(); + showAddWearablesPanel(!current_visibility); } -void LLPanelOutfitEdit::showAddWearablesPanel() +void LLPanelOutfitEdit::showAddWearablesPanel(bool show_add_wearables) { - childSetVisible("add_wearables_panel", childGetValue("add_btn")); + mAddWearablesPanel->setVisible(show_add_wearables); + + getChild<LLUICtrl>("show_add_wearables_btn")->setValue(show_add_wearables); + + updateFiltersVisibility(); + getChildView("filter_button")->setVisible( show_add_wearables); + + //search filter should be disabled + if (!show_add_wearables) + { + getChild<LLUICtrl>("filter_button")->setValue(false); + + mFolderViewFilterCmbBox->setVisible(false); + mListViewFilterCmbBox->setVisible(false); + + showWearablesFilter(); + + /* + * By default AT_CLOTHING are sorted by (in in MY OUTFITS): + * - by type (types order determined in LLWearableType::EType) + * - each LLWearableType::EType by outer layer on top + * + * In Add More panel AT_CLOTHING should be sorted in a such way: + * - by type (types order determined in LLWearableType::EType) + * - each LLWearableType::EType by name (EXT-8205) + */ + mWearableItemsList->setSortOrder(LLWearableItemsList::E_SORT_BY_TYPE_NAME); + + // Reset mWearableItemsList position to top. See EXT-8180. + mWearableItemsList->goToTop(); + } + + //switching button bars + getChildView("no_add_wearables_button_bar")->setVisible( !show_add_wearables); + getChildView("add_wearables_button_bar")->setVisible( show_add_wearables); } void LLPanelOutfitEdit::showWearablesFilter() { - childSetVisible("filter_combobox_panel", childGetValue("filter_button")); + bool filter_visible = getChild<LLUICtrl>("filter_button")->getValue(); + + getChildView("filter_panel")->setVisible( filter_visible); + + if(!filter_visible) + { + mSearchFilter->clear(); + onSearchEdit(LLStringUtil::null); + } + else + { + mSearchFilter->setFocus(TRUE); + } } -void LLPanelOutfitEdit::saveOutfit(bool as_new) +void LLPanelOutfitEdit::showWearablesListView() { - if (!as_new && LLAppearanceMgr::getInstance()->updateBaseOutfit()) + if(switchPanels(mInventoryItemsPanel, mWearablesListViewPanel)) { - // we don't need to ask for an outfit name, and updateBaseOutfit() successfully saved. - // If updateBaseOutfit fails, ask for an outfit name anyways - return; + updateWearablesPanelVerbButtons(); + updateFiltersVisibility(); } + mListViewBtn->setToggleState(TRUE); +} - LLPanelOutfitsInventory* panel_outfits_inventory = LLPanelOutfitsInventory::findInstance(); - if (panel_outfits_inventory) +void LLPanelOutfitEdit::showWearablesFolderView() +{ + if(switchPanels(mWearablesListViewPanel, mInventoryItemsPanel)) { - panel_outfits_inventory->onSave(); + updateWearablesPanelVerbButtons(); + updateFiltersVisibility(); } + mFolderViewBtn->setToggleState(TRUE); } -void LLPanelOutfitEdit::showSaveMenu() +void LLPanelOutfitEdit::updateFiltersVisibility() { - S32 x, y; - LLUI::getMousePositionLocal(this, &x, &y); - - mSaveMenu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, mSaveMenu, x, y); + mListViewFilterCmbBox->setVisible(mWearablesListViewPanel->getVisible()); + mFolderViewFilterCmbBox->setVisible(mInventoryItemsPanel->getVisible()); } -void LLPanelOutfitEdit::onTypeFilterChanged(LLUICtrl* ctrl) +void LLPanelOutfitEdit::onFolderViewFilterCommitted(LLUICtrl* ctrl) { - LLComboBox* type_filter = dynamic_cast<LLComboBox*>(ctrl); - llassert(type_filter); - if (type_filter) - { - U32 curr_filter_type = type_filter->getCurrentIndex(); - mInventoryItemsPanel->setFilterTypes(mLookItemTypes[curr_filter_type].inventoryMask); - } - + S32 curr_filter_type = mFolderViewFilterCmbBox->getCurrentIndex(); + if (curr_filter_type < 0) return; + + mInventoryItemsPanel->setFilterTypes(mFolderViewItemTypes[curr_filter_type].inventoryMask); + mSavedFolderState->setApply(TRUE); mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -287,6 +689,14 @@ void LLPanelOutfitEdit::onTypeFilterChanged(LLUICtrl* ctrl) LLInventoryModelBackgroundFetch::instance().start(); } +void LLPanelOutfitEdit::onListViewFilterCommitted(LLUICtrl* ctrl) +{ + S32 curr_filter_type = mListViewFilterCmbBox->getCurrentIndex(); + if (curr_filter_type < 0) return; + + mWearableListManager->setFilterCollector(mListViewItemTypes[curr_filter_type]->collector); +} + void LLPanelOutfitEdit::onSearchEdit(const std::string& string) { if (mSearchString != string) @@ -301,7 +711,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) if (mSearchString == "") { mInventoryItemsPanel->setFilterSubString(LLStringUtil::null); - + mWearableItemsList->setFilterSubString(LLStringUtil::null); // re-open folders that were initially open mSavedFolderState->setApply(TRUE); mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -327,124 +737,230 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) // set new filter string mInventoryItemsPanel->setFilterSubString(mSearchString); + mWearableItemsList->setFilterSubString(mSearchString); + } -void LLPanelOutfitEdit::onAddToOutfitClicked(void) +void LLPanelOutfitEdit::onPlusBtnClicked(void) { - LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); - if (!curr_item) return; + uuid_vec_t selected_items; + getSelectedItemsUUID(selected_items); - LLFolderViewEventListener* listenerp = curr_item->getListener(); - if (!listenerp) return; - - if (LLAppearanceMgr::getInstance()->wearItemOnAvatar(listenerp->getUUID())) + LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy; + + for(uuid_vec_t::iterator iter = selected_items.begin(); iter != selected_items.end(); iter++) { - updateLookInfo(); + LLUUID selected_id = *iter; + if (!selected_id.isNull()) + { + //replacing instead of adding the item + LLAppearanceMgr::getInstance()->wearItemOnAvatar(selected_id, false, true, link_waiter); + } } } - -void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void) +void LLPanelOutfitEdit::onVisibilityChange(const LLSD &in_visible_chain) { - LLUUID id_to_remove = mLookContents->getSelectionInterface()->getCurrentID(); - - LLAppearanceMgr::getInstance()->removeItemFromAvatar(id_to_remove); - - updateLookInfo(); + showAddWearablesPanel(false); + mWearableItemsList->resetSelection(); + mInventoryItemsPanel->clearSelection(); - mRemoveFromOutfitBtn->setEnabled(FALSE); + if (in_visible_chain.asBoolean()) + { + update(); + } } - -void LLPanelOutfitEdit::onUpClicked(void) +void LLPanelOutfitEdit::onAddWearableClicked(void) { - LLUUID inv_id = mLookContents->getSelectionInterface()->getCurrentID(); - if (inv_id.isNull()) + LLPanelDummyClothingListItem* item = dynamic_cast<LLPanelDummyClothingListItem*>(mCOFWearables->getSelectedItem()); + + if(item) { - //nothing selected, do nothing - return; + showFilteredWearablesListView(item->getWearableType()); } +} - LLViewerInventoryItem *link_item = gInventory.getItem(inv_id); - if (!link_item) +void LLPanelOutfitEdit::onReplaceMenuItemClicked(LLUUID selected_item_id) +{ + LLViewerInventoryItem* item = gInventory.getLinkedItem(selected_item_id); + + if (item) { - llwarns << "could not find inventory item based on currently worn link." << llendl; - return; + showFilteredWearablesListView(item->getWearableType()); } +} +void LLPanelOutfitEdit::onShopButtonClicked() +{ + static LLShopURLDispatcher url_resolver; + + // will contain the resultant URL + std::string url; - LLUUID asset_id = link_item->getAssetUUID(); - if (asset_id.isNull()) + if (isAgentAvatarValid()) { - llwarns << "inventory link has null Asset ID. could not get object reference" << llendl; + // try to get wearable type from 'Add More' panel first (EXT-7639) + selection_info_t selection_info = getAddMorePanelSelectionType(); + + LLWearableType::EType type = selection_info.first; + + if (selection_info.second > 1) + { + // the second argument is not important in this case: generic market place will be opened + url = url_resolver.resolveURL(LLWearableType::WT_NONE, SEX_FEMALE); + } + else + { + if (type == LLWearableType::WT_NONE) + { + type = getCOFWearablesSelectionType(); + } + + ESex sex = gAgentAvatarp->getSex(); + + // WT_INVALID comes for attachments + if (type != LLWearableType::WT_INVALID && type != LLWearableType::WT_NONE) + { + url = url_resolver.resolveURL(type, sex); + } + + if (url.empty()) + { + url = url_resolver.resolveURL( + mCOFWearables->getExpandedAccordionAssetType(), sex); + } + } + } + else + { + llwarns << "Agent avatar is invalid" << llendl; + + // the second argument is not important in this case: generic market place will be opened + url = url_resolver.resolveURL(LLWearableType::WT_NONE, SEX_FEMALE); } - static const std::string empty = ""; - LLWearableList::instance().getAsset(asset_id, - empty, // don't care about wearable name - link_item->getActualType(), - LLSidepanelAppearance::editWearable, - (void*)getParentUICtrl()); + LLWeb::loadURLExternal(url); } +LLWearableType::EType LLPanelOutfitEdit::getCOFWearablesSelectionType() const +{ + std::vector<LLPanel*> selected_items; + LLWearableType::EType type = LLWearableType::WT_NONE; + + mCOFWearables->getSelectedItems(selected_items); -void LLPanelOutfitEdit::onEditWearableClicked(void) + if (selected_items.size() == 1) + { + LLPanel* item = selected_items.front(); + + // LLPanelDummyClothingListItem is lower then LLPanelInventoryListItemBase in hierarchy tree + if (LLPanelDummyClothingListItem* dummy_item = dynamic_cast<LLPanelDummyClothingListItem*>(item)) + { + type = dummy_item->getWearableType(); + } + else if (LLPanelInventoryListItemBase* real_item = dynamic_cast<LLPanelInventoryListItemBase*>(item)) + { + type = real_item->getWearableType(); + } + } + + return type; +} + +LLPanelOutfitEdit::selection_info_t LLPanelOutfitEdit::getAddMorePanelSelectionType() const { - LLUUID id_to_edit = mLookContents->getSelectionInterface()->getCurrentID(); - LLViewerInventoryItem * item_to_edit = gInventory.getItem(id_to_edit); + selection_info_t result = std::make_pair(LLWearableType::WT_NONE, 0); - if (item_to_edit) + if (mAddWearablesPanel != NULL && mAddWearablesPanel->getVisible()) { - // returns null if not a wearable (attachment, etc). - LLWearable* wearable_to_edit = gAgentWearables.getWearableFromAssetID(item_to_edit->getAssetUUID()); - if(wearable_to_edit) + if (mInventoryItemsPanel != NULL && mInventoryItemsPanel->getVisible()) { - bool can_modify = false; - bool is_complete = item_to_edit->isFinished(); - // if item_to_edit is a link, its properties are not appropriate, - // lets get original item with actual properties - LLViewerInventoryItem* original_item = gInventory.getItem(wearable_to_edit->getItemID()); - if(original_item) + std::set<LLUUID> selected_uuids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + + result.second = selected_uuids.size(); + + if (result.second == 1) { - can_modify = original_item->getPermissions().allowModifyBy(gAgentID); - is_complete = original_item->isFinished(); + result.first = getWearableTypeByItemUUID(*(selected_uuids.begin())); } + } + else if (mWearableItemsList != NULL && mWearableItemsList->getVisible()) + { + std::vector<LLUUID> selected_uuids; + mWearableItemsList->getSelectedUUIDs(selected_uuids); - if (can_modify && is_complete) - { - LLSidepanelAppearance::editWearable(wearable_to_edit, getParent()); - if (mEditWearableBtn->getVisible()) - { - mEditWearableBtn->setVisible(FALSE); - } + result.second = selected_uuids.size(); + + if (result.second == 1) + { + result.first = getWearableTypeByItemUUID(selected_uuids.front()); } } } + + return result; +} + +LLWearableType::EType LLPanelOutfitEdit::getWearableTypeByItemUUID(const LLUUID& item_uuid) const +{ + LLViewerInventoryItem* item = gInventory.getLinkedItem(item_uuid); + return (item != NULL) ? item->getWearableType() : LLWearableType::WT_NONE; +} + +void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void) +{ + LLUUID id_to_remove = mCOFWearables->getSelectedUUID(); + LLWearableType::EType type = getWearableTypeByItemUUID(id_to_remove); + + LLAppearanceMgr::getInstance()->removeItemFromAvatar(id_to_remove); + + if (!mCOFWearables->getSelectedItem()) + { + mCOFWearables->selectClothing(type); + } +} + + +void LLPanelOutfitEdit::onEditWearableClicked(void) +{ + LLUUID selected_item_id = mCOFWearables->getSelectedUUID(); + if (selected_item_id.notNull()) + { + gAgentWearables.editWearable(selected_item_id); + } } -void LLPanelOutfitEdit::onInventorySelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action) +void LLPanelOutfitEdit::updatePlusButton() { - LLFolderViewItem* current_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); - if (!current_item) + uuid_vec_t selected_items; + getSelectedItemsUUID(selected_items); + if (selected_items.empty()) { + mPlusBtn->setEnabled(false); return; } - LLViewerInventoryItem* item = current_item->getInventoryItem(); - if (!item) return; + // If any of the selected items are not wearable (due to already being worn OR being of the wrong type), disable the add button. + uuid_vec_t::iterator unwearable_item = std::find_if(selected_items.begin(), selected_items.end(), !boost::bind(&get_can_item_be_worn, _1)); + bool can_add = ( unwearable_item == selected_items.end() ); - switch (item->getType()) + mPlusBtn->setEnabled(can_add); + + LLViewerInventoryItem* first_item(gInventory.getItem(selected_items.front())); + + if (can_add && + first_item && + selected_items.size() == 1 && + first_item->getType() == LLAssetType::AT_BODYPART) { - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_OBJECT: - childSetEnabled("add_to_outfit_btn", true); - break; - default: - childSetEnabled("add_to_outfit_btn", false); - break; + mPlusBtn->setToolTip(getString("replace_body_part")); } - + else + { + mPlusBtn->setToolTip(LLStringUtil::null); + } + /* Removing add to look inline button (not part of mvp for viewer 2) LLRect btn_rect(current_item->getLocalRect().mRight - 50, current_item->getLocalRect().mTop, @@ -461,98 +977,215 @@ void LLPanelOutfitEdit::onInventorySelectionChange(const std::deque<LLFolderView current_item->addChild(mAddToLookBtn); */ } -void LLPanelOutfitEdit::onOutfitItemSelectionChange(void) -{ - LLScrollListItem* item = mLookContents->getLastSelectedItem(); - if (!item) - return; - LLRect item_rect; - mLookContents->localRectToOtherView(item->getRect(), &item_rect, this); +void LLPanelOutfitEdit::applyFolderViewFilter(EFolderViewItemType type) +{ + mFolderViewFilterCmbBox->setCurrentByIndex(type); + mFolderViewFilterCmbBox->onCommit(); +} - // TODO button(and item list) should be removed (when new widget is ready) - LLRect btn_rect = mEditWearableBtn->getRect(); - btn_rect.set(item_rect.mRight - btn_rect.getWidth(), item_rect.mTop, item_rect.mRight, item_rect.mBottom); - - mEditWearableBtn->setShape(btn_rect); - sendChildToFront(mEditWearableBtn); +void LLPanelOutfitEdit::applyListViewFilter(EListViewItemType type) +{ + mListViewFilterCmbBox->setCurrentByIndex(type); + mListViewFilterCmbBox->onCommit(); +} + +void LLPanelOutfitEdit::filterWearablesBySelectedItem(void) +{ + if (!mAddWearablesPanel->getVisible()) return; - mEditWearableBtn->setEnabled(TRUE); - if (!mEditWearableBtn->getVisible()) + uuid_vec_t ids; + mCOFWearables->getSelectedUUIDs(ids); + + bool nothing_selected = ids.empty(); + bool one_selected = ids.size() == 1; + bool more_than_one_selected = ids.size() > 1; + bool is_dummy_item = (ids.size() && dynamic_cast<LLPanelDummyClothingListItem*>(mCOFWearables->getSelectedItem())); + + // selected, expanded accordion tabs and selection in flat list view determine filtering when no item is selected in COF + // selection in flat list view participates in determining filtering because of EXT-7963 + // So the priority of criterions in is: + // 1. Selected accordion tab | IF (any accordion selected) + // | filter_type = selected_accordion_type + // 2. Selected item in flat list view | ELSEIF (any item in flat list view selected) + // | filter_type = selected_item_type + // 3. Expanded accordion tab | ELSEIF (any accordion expanded) + // | filter_type = expanded accordion_type + if (nothing_selected) { - mEditWearableBtn->setVisible(TRUE); + if (mInventoryItemsPanel->getVisible()) + { + return; + } + showWearablesListView(); + + //selected accordion tab is more priority than expanded tab + //and selected item in flat list view of 'Add more' panel when + //determining filtering + LLAssetType::EType type = mCOFWearables->getSelectedAccordionAssetType(); + if (type == LLAssetType::AT_NONE) + { //no accordion selected + + // when no accordion selected then selected item from flat list view + // has more priority than expanded when determining filtering + LLUUID selected_item_id = mWearableItemsList->getSelectedUUID(); + LLViewerInventoryItem* item = gInventory.getLinkedItem(selected_item_id); + if(item) + { + showFilteredWearablesListView(item->getWearableType()); + return; + } + + // when no accordion selected and no selected items in flat list view + // determine filtering according to expanded accordion + type = mCOFWearables->getExpandedAccordionAssetType(); + } + + switch (type) + { + case LLAssetType::AT_OBJECT: + applyListViewFilter(LVIT_ATTACHMENT); + break; + case LLAssetType::AT_BODYPART: + applyListViewFilter(LVIT_BODYPART); + break; + case LLAssetType::AT_CLOTHING: + default: + applyListViewFilter(LVIT_CLOTHING); + break; + } + + return; } + //resetting selection if more than one item is selected + if (more_than_one_selected) + { + if (mInventoryItemsPanel->getVisible()) + { + applyFolderViewFilter(FVIT_ALL); + return; + } + + showWearablesListView(); + applyListViewFilter(LVIT_ALL); + return; + } - const LLUUID& id_item_to_remove = item->getUUID(); - LLViewerInventoryItem* item_to_remove = gInventory.getItem(id_item_to_remove); - if (!item_to_remove) return; - switch (item_to_remove->getType()) + //filter wearables by a type represented by a dummy item + if (one_selected && is_dummy_item) { - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_OBJECT: - mRemoveFromOutfitBtn->setEnabled(TRUE); - break; - default: - mRemoveFromOutfitBtn->setEnabled(FALSE); - break; + if (mInventoryItemsPanel->getVisible()) + { + applyFolderViewFilter(FVIT_WEARABLE); + return; + } + + onAddWearableClicked(); + return; } + + LLViewerInventoryItem* item = gInventory.getItem(ids[0]); + if (!item && ids[0].notNull()) + { + if (mInventoryItemsPanel->getVisible()) + { + applyFolderViewFilter(FVIT_ALL); + return; + } + //Inventory misses an item with non-zero id + showWearablesListView(); + applyListViewFilter(LVIT_ALL); + return; + } + + if (item && one_selected && !is_dummy_item) + { + if (item->isWearableType()) + { + if (mInventoryItemsPanel->getVisible()) + { + applyFolderViewFilter(FVIT_WEARABLE); + return; + } + //single clothing or bodypart item is selected + showFilteredWearablesListView(item->getWearableType()); + return; + } + else + { + if (mInventoryItemsPanel->getVisible()) + { + applyFolderViewFilter(FVIT_ATTACHMENT); + return; + } + //attachment is selected + showWearablesListView(); + applyListViewFilter(LVIT_ATTACHMENT); + return; + } + } + } -void LLPanelOutfitEdit::changed(U32 mask) + + +void LLPanelOutfitEdit::update() { + mCOFWearables->refresh(); + + updateVerbs(); } -void LLPanelOutfitEdit::lookFetched(void) +BOOL LLPanelOutfitEdit::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) { - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; + if (cargo_data == NULL) + { + llwarns << "cargo_data is NULL" << llendl; + return TRUE; + } - // collectDescendentsIf takes non-const reference: - LLFindCOFValidItems is_cof_valid; - gInventory.collectDescendentsIf(mCurrentOutfitID, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_cof_valid); - for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); - iter != item_array.end(); - iter++) + switch (cargo_type) { - const LLViewerInventoryItem *item = (*iter); - - LLSD row; - row["id"] = item->getUUID(); - LLSD& columns = row["columns"]; - columns[0]["column"] = "look_item"; - columns[0]["type"] = "text"; - columns[0]["value"] = item->getName(); - columns[1]["column"] = "look_item_sort"; - columns[1]["type"] = "text"; // TODO: multi-wearable sort "type" should go here. - columns[1]["value"] = "BAR"; // TODO: Multi-wearable sort index should go here - - mLookContents->addElement(row); + case DAD_BODYPART: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_LINK: + *accept = ACCEPT_YES_MULTI; + break; + default: + *accept = ACCEPT_NO; } -} -void LLPanelOutfitEdit::updateLookInfo() -{ - if (getVisible()) + if (drop) { - mLookContents->clearRows(); + LLInventoryItem* item = static_cast<LLInventoryItem*>(cargo_data); - mFetchLook->setFetchID(mCurrentOutfitID); - mFetchLook->startFetch(); - if (mFetchLook->isFinished()) + if (LLAssetType::lookupIsAssetIDKnowable(item->getType())) { - mFetchLook->done(); + mCOFDragAndDropObserver->watchAsset(item->getAssetUUID()); + + /* + * Adding request to wear item. If the item is a link, then getLinkedUUID() will + * return the ID of the linked item. Otherwise it will return the item's ID. The + * second argument is used to delay the appearance update until all dragged items + * are added to optimize user experience. + */ + LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), false); } else { - gInventory.addObserver(mFetchLook); + // if asset id is not available for the item we must wear it immediately (attachments only) + LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), true); } } + + return TRUE; } void LLPanelOutfitEdit::displayCurrentOutfit() @@ -562,8 +1195,13 @@ void LLPanelOutfitEdit::displayCurrentOutfit() setVisible(TRUE); } - mCurrentOutfitID = LLAppearanceMgr::getInstance()->getCOF(); + updateCurrentOutfitName(); + + update(); +} +void LLPanelOutfitEdit::updateCurrentOutfitName() +{ std::string current_outfit_name; if (LLAppearanceMgr::getInstance()->getBaseOutfitName(current_outfit_name)) { @@ -573,8 +1211,227 @@ void LLPanelOutfitEdit::displayCurrentOutfit() { mCurrentOutfitName->setText(getString("No Outfit")); } +} + +//private +void LLPanelOutfitEdit::updateVerbs() +{ + bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); + bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked(); + bool has_baseoutfit = LLAppearanceMgr::getInstance()->getBaseOutfitUUID().notNull(); + + mSaveComboBtn->setSaveBtnEnabled(!outfit_locked && outfit_is_dirty); + getChildView(REVERT_BTN)->setEnabled(outfit_is_dirty && has_baseoutfit); + + mSaveComboBtn->setMenuItemEnabled("save_outfit", !outfit_locked && outfit_is_dirty); + + mStatus->setText(outfit_is_dirty ? getString("unsaved_changes") : getString("now_editing")); + + updateCurrentOutfitName(); + + //updating state of "Wear Item" button previously known as "Plus" button + updatePlusButton(); +} + +bool LLPanelOutfitEdit::switchPanels(LLPanel* switch_from_panel, LLPanel* switch_to_panel) +{ + if(switch_from_panel && switch_to_panel && !switch_to_panel->getVisible()) + { + switch_from_panel->setVisible(FALSE); + switch_to_panel->setVisible(TRUE); + return true; + } + return false; +} + +void LLPanelOutfitEdit::resetAccordionState() +{ + if (mCOFWearables != NULL) + { + mCOFWearables->expandDefaultAccordionTab(); + } + else + { + llwarns << "mCOFWearables is NULL" << llendl; + } +} + +void LLPanelOutfitEdit::onGearButtonClick(LLUICtrl* clicked_button) +{ + LLMenuGL* menu = NULL; + + if (mAddWearablesPanel->getVisible()) + { + if (!mAddWearablesGearMenu) + { + mAddWearablesGearMenu = LLAddWearablesGearMenu::create(mWearableItemsList, mInventoryItemsPanel); + } + + menu = mAddWearablesGearMenu; + } + else + { + if (!mGearMenu) + { + mGearMenu = LLPanelOutfitEditGearMenu::create(); + } + + menu = mGearMenu; + } + + if (!menu) return; + + menu->arrangeAndClear(); // update menu height + S32 menu_y = menu->getRect().getHeight() + clicked_button->getRect().getHeight(); + menu->buildDrawLabels(); + LLMenuGL::showPopup(clicked_button, menu, 0, menu_y); +} + +void LLPanelOutfitEdit::onAddMoreButtonClicked() +{ + toggleAddWearablesPanel(); + filterWearablesBySelectedItem(); +} + +void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type) +{ + showAddWearablesPanel(true); + showWearablesListView(); + + //e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE + applyListViewFilter((EListViewItemType) (LVIT_SHAPE + type)); +} + +static void update_status_widget_rect(LLView * widget, S32 right_border) +{ + LLRect rect = widget->getRect(); + rect.mRight = right_border; + + widget->setShape(rect); +} + +void LLPanelOutfitEdit::onOutfitChanging(bool started) +{ + static LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("edit_outfit_loading_indicator"); + static LLView* status_panel = getChild<LLView>("outfit_name_and_status"); + static S32 indicator_delta = status_panel->getRect().getWidth() - indicator->getRect().mLeft; + + S32 delta = started ? indicator_delta : 0; + S32 right_border = status_panel->getRect().getWidth() - delta; + + update_status_widget_rect(mCurrentOutfitName, right_border); + update_status_widget_rect(mStatus, right_border); + + indicator->setVisible(started); +} + +void LLPanelOutfitEdit::getCurrentItemUUID(LLUUID& selected_id) +{ + if (mInventoryItemsPanel->getVisible()) + { + LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); + if (!curr_item) return; + + LLFolderViewEventListener* listenerp = curr_item->getListener(); + if (!listenerp) return; + + selected_id = listenerp->getUUID(); + } + else if (mWearablesListViewPanel->getVisible()) + { + selected_id = mWearableItemsList->getSelectedUUID(); + } +} + + +void LLPanelOutfitEdit::getSelectedItemsUUID(uuid_vec_t& uuid_list) +{ + if (mInventoryItemsPanel->getVisible()) + { + std::set<LLUUID> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + + std::for_each(item_set.begin(), item_set.end(), boost::bind( &uuid_vec_t::push_back, &uuid_list, _1)); + } + else if (mWearablesListViewPanel->getVisible()) + { + std::vector<LLSD> item_set; + mWearableItemsList->getSelectedValues(item_set); + + std::for_each(item_set.begin(), item_set.end(), boost::bind( &uuid_vec_t::push_back, &uuid_list, boost::bind(&LLSD::asUUID, _1 ))); + + } + +// return selected_id; +} + +void LLPanelOutfitEdit::onCOFChanged() +{ + //the panel is only updated when is visible to a user + + // BAP - this check has to be removed because otherwise item name + // changes made when the panel is not visible will not be + // propagated to the panel. + // if (!isInVisibleChain()) return; - updateLookInfo(); + update(); } +void LLPanelOutfitEdit::updateWearablesPanelVerbButtons() +{ + if(mWearablesListViewPanel->getVisible()) + { + mFolderViewBtn->setToggleState(FALSE); + mFolderViewBtn->setImageOverlay(getString("folder_view_off"), mFolderViewBtn->getImageOverlayHAlign()); + mListViewBtn->setImageOverlay(getString("list_view_on"), mListViewBtn->getImageOverlayHAlign()); + } + else if(mInventoryItemsPanel->getVisible()) + { + mListViewBtn->setToggleState(FALSE); + mListViewBtn->setImageOverlay(getString("list_view_off"), mListViewBtn->getImageOverlayHAlign()); + mFolderViewBtn->setImageOverlay(getString("folder_view_on"), mFolderViewBtn->getImageOverlayHAlign()); + } +} + +void LLPanelOutfitEdit::saveListSelection() +{ + if(mWearablesListViewPanel->getVisible()) + { + std::set<LLUUID> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + + if(!selected_ids.size()) return; + + for (std::set<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) + { + mWearableItemsList->selectItemByUUID(*item_id, true); + } + mWearableItemsList->scrollToShowFirstSelectedItem(); + } + else if(mInventoryItemsPanel->getVisible()) + { + std::vector<LLUUID> selected_ids; + mWearableItemsList->getSelectedUUIDs(selected_ids); + + if(!selected_ids.size()) return; + + mInventoryItemsPanel->clearSelection(); + LLFolderView* root = mInventoryItemsPanel->getRootFolder(); + + if(!root) return; + + for(std::vector<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) + { + LLFolderViewItem* item = root->getItemByID(*item_id); + if (!item) continue; + + LLFolderViewFolder* parent = item->getParentFolder(); + if(parent) + { + parent->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + } + mInventoryItemsPanel->getRootFolder()->changeSelection(item, TRUE); + } + mInventoryItemsPanel->getRootFolder()->scrollToShowSelection(); + } +} +// EOF |