diff options
Diffstat (limited to 'indra/newview/llpaneloutfitedit.cpp')
-rw-r--r-- | indra/newview/llpaneloutfitedit.cpp | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp new file mode 100644 index 0000000000..d78a448acb --- /dev/null +++ b/indra/newview/llpaneloutfitedit.cpp @@ -0,0 +1,575 @@ +/** + * @file llpaneloutfitedit.cpp + * @brief Displays outfit edit information in Side Tray. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpaneloutfitedit.h" + +// *TODO: reorder includes to match the coding standard +#include "llagent.h" +#include "llagentwearables.h" +#include "llappearancemgr.h" +#include "llinventory.h" +#include "llviewercontrol.h" +#include "llui.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "llinventoryfunctions.h" +#include "llinventorypanel.h" +#include "llviewermenu.h" +#include "llviewerwindow.h" +#include "llviewerinventory.h" +#include "llbutton.h" +#include "llcombobox.h" +#include "llfiltereditor.h" +#include "llfloaterinventory.h" +#include "llinventorybridge.h" +#include "llinventorymodel.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llpaneloutfitsinventory.h" +#include "lluiconstants.h" +#include "llscrolllistctrl.h" +#include "lltextbox.h" +#include "lluictrlfactory.h" +#include "llsdutil.h" +#include "llsidepanelappearance.h" +#include "lltoggleablemenu.h" +#include "llwearablelist.h" + +static LLRegisterPanelClassWrapper<LLPanelOutfitEdit> t_outfit_edit("panel_outfit_edit"); + +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 +{ +public: + LLInventoryLookObserver(LLPanelOutfitEdit *panel) : mPanel(panel) {} + virtual ~LLInventoryLookObserver() + { + if (gInventory.containsObserver(this)) + { + gInventory.removeObserver(this); + } + } + + virtual void changed(U32 mask) + { + if (mask & (LLInventoryObserver::ADD | LLInventoryObserver::REMOVE)) + { + mPanel->updateLookInfo(); + } + } +protected: + LLPanelOutfitEdit *mPanel; +}; + +class LLLookFetchObserver : public LLInventoryFetchDescendentsObserver +{ +public: + LLLookFetchObserver(LLPanelOutfitEdit *panel) : + mPanel(panel) + {} + LLLookFetchObserver() {} + virtual void done() + { + mPanel->lookFetched(); + if(gInventory.containsObserver(this)) + { + gInventory.removeObserver(this); + } + } +private: + LLPanelOutfitEdit *mPanel; +}; + + + +LLPanelOutfitEdit::LLPanelOutfitEdit() +: LLPanel(), mCurrentOutfitID(), mFetchLook(NULL), mSearchFilter(NULL), +mLookContents(NULL), mInventoryItemsPanel(NULL), mAddToOutfitBtn(NULL), +mRemoveFromOutfitBtn(NULL), mLookObserver(NULL) +{ + mSavedFolderState = new LLSaveFolderState(); + mSavedFolderState->setApply(FALSE); + + mFetchLook = new LLLookFetchObserver(this); + mLookObserver = new LLInventoryLookObserver(this); + gInventory.addObserver(mLookObserver); + + mLookItemTypes.reserve(NUM_LOOK_ITEM_TYPES); + for (U32 i = 0; i < NUM_LOOK_ITEM_TYPES; i++) + { + mLookItemTypes.push_back(LLLookItemType()); + } + + // TODO: make these strings translatable + mLookItemTypes[LIT_ALL] = LLLookItemType("All Items", ALL_ITEMS_MASK); + mLookItemTypes[LIT_WEARABLE] = LLLookItemType("Shape & Clothing", WEARABLE_MASK); + mLookItemTypes[LIT_ATTACHMENT] = LLLookItemType("Attachments", ATTACHMENT_MASK); + +} + +LLPanelOutfitEdit::~LLPanelOutfitEdit() +{ + delete mSavedFolderState; + if (gInventory.containsObserver(mFetchLook)) + { + gInventory.removeObserver(mFetchLook); + } + delete mFetchLook; + + if (gInventory.containsObserver(mLookObserver)) + { + gInventory.removeObserver(mLookObserver); + } + delete mLookObserver; +} + +BOOL LLPanelOutfitEdit::postBuild() +{ + // gInventory.isInventoryUsable() no longer needs to be tested per Richard's fix for race conditions between inventory and panels + + mCurrentOutfitName = getChild<LLTextBox>("curr_outfit_name"); + + childSetCommitCallback("add_btn", boost::bind(&LLPanelOutfitEdit::showAddWearablesPanel, this), NULL); + + mLookContents = getChild<LLScrollListCtrl>("look_items_list"); + mLookContents->sortByColumn("look_item_sort", TRUE); + mLookContents->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onOutfitItemSelectionChange, this)); + + mInventoryItemsPanel = getChild<LLInventoryPanel>("inventory_items"); + 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>("inventory_filter"); + type_filter->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onTypeFilterChanged, this, _1)); + type_filter->removeall(); + for (U32 i = 0; i < mLookItemTypes.size(); ++i) + { + type_filter->add(mLookItemTypes[i].displayName); + } + type_filter->setCurrentByIndex(LIT_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)); + + 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("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)); + + 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()); + + return TRUE; +} + +void LLPanelOutfitEdit::showAddWearablesPanel() +{ + childSetVisible("add_wearables_panel", childGetValue("add_btn")); +} + +void LLPanelOutfitEdit::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; + } + + LLPanelOutfitsInventory* panel_outfits_inventory = LLPanelOutfitsInventory::findInstance(); + if (panel_outfits_inventory) + { + panel_outfits_inventory->onSave(); + } +} + +void LLPanelOutfitEdit::showSaveMenu() +{ + S32 x, y; + LLUI::getMousePositionLocal(this, &x, &y); + + mSaveMenu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, mSaveMenu, x, y); +} + +void LLPanelOutfitEdit::onTypeFilterChanged(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); + } + + mSavedFolderState->setApply(TRUE); + mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); + + LLOpenFoldersWithSelection opener; + mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener); + mInventoryItemsPanel->getRootFolder()->scrollToShowSelection(); + + LLInventoryModelBackgroundFetch::instance().start(); +} + +void LLPanelOutfitEdit::onSearchEdit(const std::string& string) +{ + if (mSearchString != string) + { + mSearchString = string; + + // Searches are case-insensitive + LLStringUtil::toUpper(mSearchString); + LLStringUtil::trimHead(mSearchString); + } + + if (mSearchString == "") + { + mInventoryItemsPanel->setFilterSubString(LLStringUtil::null); + + // re-open folders that were initially open + mSavedFolderState->setApply(TRUE); + mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); + LLOpenFoldersWithSelection opener; + mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener); + mInventoryItemsPanel->getRootFolder()->scrollToShowSelection(); + } + + LLInventoryModelBackgroundFetch::instance().start(); + + if (mInventoryItemsPanel->getFilterSubString().empty() && mSearchString.empty()) + { + // current filter and new filter empty, do nothing + return; + } + + // save current folder open state if no filter currently applied + if (mInventoryItemsPanel->getRootFolder()->getFilterSubString().empty()) + { + mSavedFolderState->setApply(FALSE); + mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); + } + + // set new filter string + mInventoryItemsPanel->setFilterSubString(mSearchString); +} + +void LLPanelOutfitEdit::onAddToOutfitClicked(void) +{ + LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); + if (!curr_item) return; + + LLFolderViewEventListener* listenerp = curr_item->getListener(); + if (!listenerp) return; + + if (LLAppearanceMgr::getInstance()->wearItemOnAvatar(listenerp->getUUID())) + { + updateLookInfo(); + } +} + + +void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void) +{ + LLUUID id_to_remove = mLookContents->getSelectionInterface()->getCurrentID(); + + LLAppearanceMgr::getInstance()->removeItemFromAvatar(id_to_remove); + + updateLookInfo(); + + mRemoveFromOutfitBtn->setEnabled(FALSE); +} + + +void LLPanelOutfitEdit::onUpClicked(void) +{ + LLUUID inv_id = mLookContents->getSelectionInterface()->getCurrentID(); + if (inv_id.isNull()) + { + //nothing selected, do nothing + return; + } + + LLViewerInventoryItem *link_item = gInventory.getItem(inv_id); + if (!link_item) + { + llwarns << "could not find inventory item based on currently worn link." << llendl; + return; + } + + + LLUUID asset_id = link_item->getAssetUUID(); + if (asset_id.isNull()) + { + llwarns << "inventory link has null Asset ID. could not get object reference" << llendl; + } + + static const std::string empty = ""; + LLWearableList::instance().getAsset(asset_id, + empty, // don't care about wearable name + link_item->getActualType(), + LLSidepanelAppearance::editWearable, + (void*)getParentUICtrl()); +} + + +void LLPanelOutfitEdit::onEditWearableClicked(void) +{ + LLUUID id_to_edit = mLookContents->getSelectionInterface()->getCurrentID(); + LLViewerInventoryItem * item_to_edit = gInventory.getItem(id_to_edit); + + if (item_to_edit) + { + // returns null if not a wearable (attachment, etc). + LLWearable* wearable_to_edit = gAgentWearables.getWearableFromAssetID(item_to_edit->getAssetUUID()); + if(wearable_to_edit) + { + 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) + { + can_modify = original_item->getPermissions().allowModifyBy(gAgentID); + is_complete = original_item->isFinished(); + } + + if (can_modify && is_complete) + { + LLSidepanelAppearance::editWearable(wearable_to_edit, getParent()); + if (mEditWearableBtn->getVisible()) + { + mEditWearableBtn->setVisible(FALSE); + } + } + } + } +} + +void LLPanelOutfitEdit::onInventorySelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action) +{ + LLFolderViewItem* current_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); + if (!current_item) + { + return; + } + + LLViewerInventoryItem* item = current_item->getInventoryItem(); + if (!item) return; + + switch (item->getType()) + { + 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; + } + + /* 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, + current_item->getLocalRect().mRight - 30, + current_item->getLocalRect().mBottom); + + mAddToLookBtn->setRect(btn_rect); + mAddToLookBtn->setEnabled(TRUE); + if (!mAddToLookBtn->getVisible()) + { + mAddToLookBtn->setVisible(TRUE); + } + + current_item->addChild(mAddToLookBtn); */ +} + +void LLPanelOutfitEdit::onOutfitItemSelectionChange(void) +{ + S32 left_offset = -4; + S32 top_offset = -10; + LLScrollListItem* item = mLookContents->getLastSelectedItem(); + if (!item) + return; + + LLRect rect = item->getRect(); + LLRect btn_rect( + left_offset + rect.mRight - 50, + top_offset + rect.mTop, + left_offset + rect.mRight - 30, + top_offset + rect.mBottom); + + mEditWearableBtn->setRect(btn_rect); + + mEditWearableBtn->setEnabled(TRUE); + if (!mEditWearableBtn->getVisible()) + { + mEditWearableBtn->setVisible(TRUE); + } + + + 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()) + { + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_OBJECT: + mRemoveFromOutfitBtn->setEnabled(TRUE); + break; + default: + mRemoveFromOutfitBtn->setEnabled(FALSE); + break; + } +} + +void LLPanelOutfitEdit::changed(U32 mask) +{ +} + +void LLPanelOutfitEdit::lookFetched(void) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + // 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++) + { + 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); + } +} + +void LLPanelOutfitEdit::updateLookInfo() +{ + if (getVisible()) + { + mLookContents->clearRows(); + + mFetchLook->setFetchID(mCurrentOutfitID); + mFetchLook->startFetch(); + if (mFetchLook->isFinished()) + { + mFetchLook->done(); + } + else + { + gInventory.addObserver(mFetchLook); + } + } +} + +void LLPanelOutfitEdit::displayCurrentOutfit() +{ + if (!getVisible()) + { + setVisible(TRUE); + } + + mCurrentOutfitID = LLAppearanceMgr::getInstance()->getCOF(); + + std::string current_outfit_name; + if (LLAppearanceMgr::getInstance()->getBaseOutfitName(current_outfit_name)) + { + mCurrentOutfitName->setText(current_outfit_name); + } + else + { + mCurrentOutfitName->setText(getString("No Outfit")); + } + + updateLookInfo(); +} + + |