/** * @file llpaneloutfitsinventory.cpp * @brief Outfits inventory panel * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llpaneloutfitsinventory.h" #include "llagentwearables.h" #include "llappearancemgr.h" #include "llfloatersidepanelcontainer.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" #include "llmenubutton.h" #include "llnotificationsutil.h" #include "lloutfitgallery.h" #include "lloutfitobserver.h" #include "lloutfitslist.h" #include "llpanelappearancetab.h" #include "llpanelwearing.h" #include "llsidepanelappearance.h" #include "lltabcontainer.h" #include "llviewercontrol.h" #include "llviewerfoldertype.h" static const std::string OUTFITS_TAB_NAME = "outfitslist_tab"; static const std::string OUTFIT_GALLERY_TAB_NAME = "outfit_gallery_tab"; static const std::string COF_TAB_NAME = "cof_tab"; static const std::string SAVE_AS_BTN("save_as_btn"); static const std::string SAVE_BTN("save_btn"); static LLPanelInjector t_inventory("panel_outfits_inventory"); LLPanelOutfitsInventory::LLPanelOutfitsInventory() : mMyOutfitsPanel(nullptr) , mCurrentOutfitPanel(nullptr) , mActivePanel(nullptr) , mAppearanceTabs(nullptr) , mInitialized(false) , mGearMenu(nullptr) , mSortMenu(nullptr) , mTrashBtn(nullptr) , mSortMenuPanel(nullptr) , mTrashMenuPanel(nullptr) { gAgentWearables.addLoadedCallback(boost::bind(&LLPanelOutfitsInventory::onWearablesLoaded, this)); gAgentWearables.addLoadingStartedCallback(boost::bind(&LLPanelOutfitsInventory::onWearablesLoading, this)); LLOutfitObserver& observer = LLOutfitObserver::instance(); observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); } LLPanelOutfitsInventory::~LLPanelOutfitsInventory() { if (mAppearanceTabs && mInitialized) { gSavedSettings.setS32("LastAppearanceTab", mAppearanceTabs->getCurrentPanelIndex()); } mGearMenuConnection.disconnect(); mSortMenuConnection.disconnect(); mTrashMenuConnection.disconnect(); } // virtual BOOL LLPanelOutfitsInventory::postBuild() { initTabPanels(); initListCommandsHandlers(); // Fetch your outfits folder so that the links are in memory. // ( This is only necessary if we want to show a warning if a user deletes an item that has a // a link in an outfit, see "ConfirmItemDeleteHasLinks". ) const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); if (outfits_cat.notNull()) { LLInventoryModelBackgroundFetch::instance().start(outfits_cat); } getChild(SAVE_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::saveOutfit, this, false)); getChild(SAVE_AS_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::saveOutfit, this, true)); return TRUE; } // virtual void LLPanelOutfitsInventory::onOpen(const LLSD& key) { if (!mInitialized) { LLSidepanelAppearance* panel_appearance = getAppearanceSP(); if (panel_appearance) { // *TODO: move these methods to LLPanelOutfitsInventory? panel_appearance->fetchInventory(); panel_appearance->refreshCurrentOutfitName(); } if (!mAppearanceTabs->selectTab(gSavedSettings.getS32("LastAppearanceTab"))) mAppearanceTabs->selectFirstTab(); mInitialized = true; } // Make sure we know which tab is selected, update the filter, // and update verbs. onTabChange(); // *TODO: Auto open the first outfit newly created so new users can see sample outfit contents /* static bool should_open_outfit = true; if (should_open_outfit && gAgent.isFirstLogin()) { LLInventoryPanel* outfits_panel = getChild(OUTFITS_TAB_NAME); if (outfits_panel) { LLUUID my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); LLFolderViewFolder* my_outfits_folder = outfits_panel->getRootFolder()->getFolderByID(my_outfits_id); if (my_outfits_folder) { LLFolderViewFolder* first_outfit = dynamic_cast(my_outfits_folder->getFirstChild()); if (first_outfit) { first_outfit->setOpen(TRUE); } } } } should_open_outfit = false; */ } void LLPanelOutfitsInventory::updateVerbs() { if (mListCommands) { updateListCommands(); } } // virtual void LLPanelOutfitsInventory::onSearchEdit(const std::string& string) { if (!mActivePanel) return; if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) { llassert(false); // this should have been done on startup LLInventoryModelBackgroundFetch::instance().start(); } // set new filter string mActivePanel->setFilterSubString(string); } void LLPanelOutfitsInventory::onWearButtonClick() { if(isOutfitsListPanelActive()) { if (mMyOutfitsPanel->hasItemSelected()) { mMyOutfitsPanel->wearSelectedItems(); } else { mMyOutfitsPanel->performAction("replaceoutfit"); } } else if(isOutfitsGalleryPanelActive()) { mOutfitGalleryPanel->wearSelectedOutfit(); } } bool LLPanelOutfitsInventory::onSaveCommit(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (0 == option) { std::string outfit_name = response["message"].asString(); LLStringUtil::trim(outfit_name); if( !outfit_name.empty() ) { LLAppearanceMgr::getInstance()->makeNewOutfitLinks(outfit_name); LLSidepanelAppearance* panel_appearance = getAppearanceSP(); if (panel_appearance) { panel_appearance->showOutfitsInventoryPanel(); } if (mAppearanceTabs) { mAppearanceTabs->selectTabByName(OUTFITS_TAB_NAME); } } } return false; } void LLPanelOutfitsInventory::onSave() { std::string outfit_name; if (!LLAppearanceMgr::getInstance()->getBaseOutfitName(outfit_name)) { outfit_name = LLViewerFolderType::lookupNewCategoryName(LLFolderType::FT_OUTFIT); } LLSD args; args["DESC"] = outfit_name; LLSD payload; //payload["ids"].append(*it); LLNotificationsUtil::add("SaveOutfitAs", args, payload, boost::bind(&LLPanelOutfitsInventory::onSaveCommit, this, _1, _2)); } //static LLPanelOutfitsInventory* LLPanelOutfitsInventory::findInstance() { return dynamic_cast(LLFloaterSidePanelContainer::getPanel("appearance", "panel_outfits_inventory")); } void LLPanelOutfitsInventory::openApearanceTab(const std::string& tab_name) { if (!mAppearanceTabs) return; mAppearanceTabs->selectTabByName(tab_name); } ////////////////////////////////////////////////////////////////////////////////// // List Commands // void LLPanelOutfitsInventory::initListCommandsHandlers() { mListCommands = getChild("bottom_panel"); mListCommands->childSetAction("wear_btn", boost::bind(&LLPanelOutfitsInventory::onWearButtonClick, this)); mMyOutfitsPanel->childSetAction("trash_btn", boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this)); mOutfitGalleryPanel->childSetAction("trash_btn", boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this)); } void LLPanelOutfitsInventory::setMenuButtons(LLMenuButton* gear_menu, LLMenuButton* sort_menu, LLButton* trash_btn, LLPanel* sort_menu_panel, LLPanel* trash_menu_panel) { mGearMenu = gear_menu; mSortMenu = sort_menu; mTrashBtn = trash_btn; mSortMenuPanel = sort_menu_panel; mTrashMenuPanel = trash_menu_panel; mGearMenuConnection.disconnect(); mSortMenuConnection.disconnect(); mTrashMenuConnection.disconnect(); mGearMenuConnection = mGearMenu->setMouseDownCallback(boost::bind(&LLPanelOutfitsInventory::onGearMouseDown, this)); mSortMenuConnection = mSortMenu->setMouseDownCallback(boost::bind(&LLPanelOutfitsInventory::onGearMouseDown, this)); mTrashMenuConnection = mTrashBtn->setClickedCallback(boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this)); } void LLPanelOutfitsInventory::updateListCommands() { bool trash_enabled = isActionEnabled("delete"); bool wear_enabled = isActionEnabled("wear"); bool wear_visible = !isCOFPanelActive(); bool make_outfit_enabled = isActionEnabled("save_outfit"); LLButton* wear_btn = mListCommands->getChild("wear_btn"); mMyOutfitsPanel->childSetEnabled("trash_btn", trash_enabled); mOutfitGalleryPanel->childSetEnabled("trash_btn", trash_enabled); wear_btn->setEnabled(wear_enabled); wear_btn->setVisible(wear_visible); getChild(SAVE_BTN)->setEnabled(make_outfit_enabled); wear_btn->setToolTip(getString((!isOutfitsGalleryPanelActive() && mMyOutfitsPanel->hasItemSelected()) ? "wear_items_tooltip" : "wear_outfit_tooltip")); } void LLPanelOutfitsInventory::onTrashButtonClick() { if(isOutfitsListPanelActive()) { mMyOutfitsPanel->removeSelected(); } else if(isOutfitsGalleryPanelActive()) { mOutfitGalleryPanel->removeSelected(); } } void LLPanelOutfitsInventory::onGearMouseDown() { if (mActivePanel) { mActivePanel->updateMenuItemsVisibility(); } } bool LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) { return mActivePanel && mActivePanel->isActionEnabled(userdata); } // List Commands // ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// // Tab panels // void LLPanelOutfitsInventory::initTabPanels() { //TODO: Add LLOutfitGallery change callback mCurrentOutfitPanel = findChild(COF_TAB_NAME); mCurrentOutfitPanel->setSelectionChangeCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); mMyOutfitsPanel = findChild(OUTFITS_TAB_NAME); mMyOutfitsPanel->setSelectionChangeCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); mOutfitGalleryPanel = findChild(OUTFIT_GALLERY_TAB_NAME); mOutfitGalleryPanel->setSelectionChangeCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); mAppearanceTabs = getChild("appearance_tabs"); mAppearanceTabs->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::onTabChange, this)); } void LLPanelOutfitsInventory::onTabChange() { if (!mAppearanceTabs) return; mActivePanel = dynamic_cast(mAppearanceTabs->getCurrentPanel()); if (!mActivePanel) return; mActivePanel->checkFilterSubString(); mActivePanel->onOpen(LLSD()); if (mGearMenu) { mGearMenu->setMenu(mActivePanel->getGearMenu(), LLMenuButton::MP_BOTTOM_LEFT); } if (mSortMenu && mSortMenuPanel) { LLToggleableMenu* menu = mActivePanel->getSortMenu(); if (menu) { mSortMenu->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT); mSortMenuPanel->setVisible(true); } else { mSortMenuPanel->setVisible(false); } } if (mTrashMenuPanel) { mTrashMenuPanel->setVisible(mActivePanel->getTrashMenuVisible()); } updateVerbs(); } bool LLPanelOutfitsInventory::isCOFPanelActive() const { if (!mActivePanel) return false; return mActivePanel->getName() == COF_TAB_NAME; } bool LLPanelOutfitsInventory::isOutfitsListPanelActive() const { if (!mActivePanel) return false; return mActivePanel->getName() == OUTFITS_TAB_NAME; } bool LLPanelOutfitsInventory::isOutfitsGalleryPanelActive() const { if (!mActivePanel) return false; return mActivePanel->getName() == OUTFIT_GALLERY_TAB_NAME; } void LLPanelOutfitsInventory::setWearablesLoading(bool val) { updateVerbs(); } void LLPanelOutfitsInventory::onWearablesLoaded() { setWearablesLoading(false); } void LLPanelOutfitsInventory::onWearablesLoading() { setWearablesLoading(true); } // static LLSidepanelAppearance* LLPanelOutfitsInventory::getAppearanceSP() { LLSidepanelAppearance* panel_appearance = dynamic_cast(LLFloaterSidePanelContainer::getPanel("appearance")); return panel_appearance; } void LLPanelOutfitsInventory::saveOutfit(bool as_new) { if (!as_new && LLAppearanceMgr::getInstance()->updateBaseOutfit()) { // we don't need to ask for an outfit name, and updateBaseOutfit() successfully saved. // If updateBaseOutfit fails, ask for an outfit name anyways return; } onSave(); }