summaryrefslogtreecommitdiff
path: root/indra/newview/llpaneloutfitedit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llpaneloutfitedit.cpp')
-rw-r--r--indra/newview/llpaneloutfitedit.cpp575
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();
+}
+
+