diff options
Diffstat (limited to 'indra/newview')
-rwxr-xr-x | indra/newview/CMakeLists.txt | 2 | ||||
-rwxr-xr-x | indra/newview/llinventorybridge.cpp | 5 | ||||
-rw-r--r-- | indra/newview/lloutfitgallery.cpp | 770 | ||||
-rw-r--r-- | indra/newview/lloutfitgallery.h | 223 | ||||
-rwxr-xr-x | indra/newview/lloutfitslist.cpp | 1178 | ||||
-rwxr-xr-x | indra/newview/lloutfitslist.h | 209 | ||||
-rwxr-xr-x | indra/newview/llpaneloutfitsinventory.cpp | 6 | ||||
-rwxr-xr-x | indra/newview/llpaneloutfitsinventory.h | 4 | ||||
-rwxr-xr-x | indra/newview/lltexturectrl.cpp | 2 | ||||
-rwxr-xr-x | indra/newview/skins/default/colors.xml | 9 | ||||
-rw-r--r-- | indra/newview/skins/default/textures/icons/Default_Outfit_Photo.png | bin | 0 -> 19459 bytes | |||
-rwxr-xr-x | indra/newview/skins/default/textures/textures.xml | 1 | ||||
-rwxr-xr-x | indra/newview/skins/default/xui/en/floater_snapshot.xml | 4 | ||||
-rwxr-xr-x | indra/newview/skins/default/xui/en/menu_outfit_gear.xml | 9 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_outfit_gallery.xml | 115 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_outfit_gallery_item.xml | 72 | ||||
-rwxr-xr-x | indra/newview/skins/default/xui/en/panel_outfits_inventory.xml | 11 |
17 files changed, 2036 insertions, 584 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 05483c4608..6567654a9d 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -412,6 +412,7 @@ set(viewer_SOURCE_FILES llnotificationscripthandler.cpp llnotificationstorage.cpp llnotificationtiphandler.cpp + lloutfitgallery.cpp lloutfitslist.cpp lloutfitobserver.cpp lloutputmonitorctrl.cpp @@ -1024,6 +1025,7 @@ set(viewer_HEADER_FILES llnotificationlistview.h llnotificationmanager.h llnotificationstorage.h + lloutfitgallery.h lloutfitslist.h lloutfitobserver.h lloutputmonitorctrl.h diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 9782c792c9..6e51c08d9c 100755 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4483,8 +4483,9 @@ static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_curr if ((inv_item->getInventoryType() != LLInventoryType::IT_WEARABLE) && (inv_item->getInventoryType() != LLInventoryType::IT_GESTURE) && (inv_item->getInventoryType() != LLInventoryType::IT_ATTACHMENT) && - (inv_item->getInventoryType() != LLInventoryType::IT_OBJECT)) - { + (inv_item->getInventoryType() != LLInventoryType::IT_OBJECT) && + (inv_item->getInventoryType() != LLInventoryType::IT_TEXTURE)) + { return FALSE; } diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp new file mode 100644 index 0000000000..c301f5d570 --- /dev/null +++ b/indra/newview/lloutfitgallery.cpp @@ -0,0 +1,770 @@ +/** + * @file lloutfitgallery.cpp + * @author Pavlo Kryvych + * @brief Visual gallery of agent's outfits for My Appearance side panel + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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" // must be first include +#include "lloutfitgallery.h" + +#include <boost/foreach.hpp> + +// llcommon +#include "llcommonutils.h" +#include "llvfile.h" + +#include "llappearancemgr.h" +#include "lleconomy.h" +#include "llerror.h" +#include "llfilepicker.h" +#include "llfloaterperms.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "lllocalbitmaps.h" +#include "llviewermenufile.h" +#include "llwearableitemslist.h" + +static LLPanelInjector<LLOutfitGallery> t_outfit_gallery("outfit_gallery"); + +LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p) + : LLOutfitListBase(), + mTexturesObserver(NULL), + mOutfitsObserver(NULL), + mScrollPanel(NULL), + mGalleryPanel(NULL), + mGalleryCreated(false), + mRowCount(0), + mItemsAddedCount(0), + mOutfitLinkPending(NULL), + mRowPanelHeight(p.row_panel_height), + mVerticalGap(p.vertical_gap), + mHorizontalGap(p.horizontal_gap), + mItemWidth(p.item_width), + mItemHeight(p.item_height), + mItemHorizontalGap(p.item_horizontal_gap), + mItemsInRow(p.items_in_row) +{ + mRowPanelWidth = p.row_panel_width_factor * mItemsInRow; + mGalleryWidth = p.gallery_width_factor * mItemsInRow; +} + +LLOutfitGallery::Params::Params() + : row_panel_height("row_panel_height", 180), + vertical_gap("vertical_gap", 10), + horizontal_gap("horizontal_gap", 10), + item_width("item_width", 150), + item_height("item_height", 175), + item_horizontal_gap("item_horizontal_gap", 16), + items_in_row("items_in_row", 3), + row_panel_width_factor("row_panel_width_factor", 166), + gallery_width_factor("gallery_width_factor", 163) +{ + addSynonym(row_panel_height, "row_height"); +} + +const LLOutfitGallery::Params& LLOutfitGallery::getDefaultParams() +{ + return LLUICtrlFactory::getDefaultParams<LLOutfitGallery>(); +} + +BOOL LLOutfitGallery::postBuild() +{ + BOOL rv = LLOutfitListBase::postBuild(); + mScrollPanel = getChild<LLScrollContainer>("gallery_scroll_panel"); + mGalleryPanel = getChild<LLPanel>("gallery_panel"); + return rv; +} + +void LLOutfitGallery::onOpen(const LLSD& info) +{ + LLOutfitListBase::onOpen(info); + if (!mGalleryCreated) + { + loadPhotos(); + uuid_vec_t cats; + getCurrentCategories(cats); + int n = cats.size(); + buildGalleryPanel(n); + mScrollPanel->addChild(mGalleryPanel); + for (int i = 0; i < n; i++) + { + addToGallery(mOutfitMap[cats[i]]); + } + mGalleryCreated = true; + } +} + +LLPanel* LLOutfitGallery::addLastRow() +{ + mRowCount++; + int row = 0; + int vgap = mVerticalGap * row; + LLPanel* result = buildRowPanel(0, row * mRowPanelHeight + vgap); + mGalleryPanel->addChild(result); + return result; +} + +void LLOutfitGallery::moveRowUp(int row) +{ + moveRow(row, mRowCount - 1 - row + 1); +} + +void LLOutfitGallery::moveRowDown(int row) +{ + moveRow(row, mRowCount - 1 - row - 1); +} + +void LLOutfitGallery::moveRow(int row, int pos) +{ + int vgap = mVerticalGap * pos; + moveRowPanel(mRowPanels[row], 0, pos * mRowPanelHeight + vgap); +} + +void LLOutfitGallery::removeLastRow() +{ + mRowCount--; + mGalleryPanel->removeChild(mLastRowPanel); + mRowPanels.pop_back(); + mLastRowPanel = mRowPanels.back(); +} + +LLPanel* LLOutfitGallery::addToRow(LLPanel* row_stack, LLOutfitGalleryItem* item, int pos, int hgap) +{ + LLPanel* lpanel = buildItemPanel(pos * mItemWidth + hgap); + lpanel->addChild(item); + row_stack->addChild(lpanel); + mItemPanels.push_back(lpanel); + return lpanel; +} + +void LLOutfitGallery::addToGallery(LLOutfitGalleryItem* item) +{ + mItemsAddedCount++; + mItemIndexMap[item] = mItemsAddedCount - 1; + int n = mItemsAddedCount; + int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; + int n_prev = n - 1; + int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; + + bool add_row = row_count != row_count_prev; + int pos = 0; + if (add_row) + { + for (int i = 0; i < row_count_prev; i++) + { + moveRowUp(i); + } + mLastRowPanel = addLastRow(); + mRowPanels.push_back(mLastRowPanel); + } + pos = (n - 1) % mItemsInRow; + mItems.push_back(item); + addToRow(mLastRowPanel, item, pos, mHorizontalGap * pos); + reshapeGalleryPanel(row_count); +} + + +void LLOutfitGallery::removeFromGalleryLast(LLOutfitGalleryItem* item) +{ + int n_prev = mItemsAddedCount; + int n = mItemsAddedCount - 1; + int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; + int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; + mItemsAddedCount--; + + bool remove_row = row_count != row_count_prev; + removeFromLastRow(mItems[mItemsAddedCount]); + mItems.pop_back(); + if (remove_row) + { + for (int i = 0; i < row_count_prev - 1; i++) + { + moveRowDown(i); + } + removeLastRow(); + } + reshapeGalleryPanel(row_count); +} + + +void LLOutfitGallery::removeFromGalleryMiddle(LLOutfitGalleryItem* item) +{ + int n = mItemIndexMap[item]; + mItemIndexMap.erase(item); + std::vector<LLOutfitGalleryItem*> saved; + for (int i = mItemsAddedCount - 1; i > n; i--) + { + saved.push_back(mItems[i]); + removeFromGalleryLast(mItems[i]); + } + removeFromGalleryLast(mItems[n]); + int saved_count = saved.size(); + for (int i = 0; i < saved_count; i++) + { + addToGallery(saved.back()); + saved.pop_back(); + } +} + +void LLOutfitGallery::removeFromLastRow(LLOutfitGalleryItem* item) +{ + mItemPanels.back()->removeChild(item); + mLastRowPanel->removeChild(mItemPanels.back()); + mItemPanels.pop_back(); +} + +LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name) +{ + LLOutfitGalleryItem::Params giparams; + LLOutfitGalleryItem* gitem = LLUICtrlFactory::create<LLOutfitGalleryItem>(giparams); + gitem->reshape(mItemWidth, mItemHeight); + gitem->setVisible(true); + gitem->setFollowsLeft(); + gitem->setFollowsTop(); + gitem->setOutfitName(name); + return gitem; +} + +void LLOutfitGallery::buildGalleryPanel(int row_count) +{ + LLPanel::Params params; + mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params); + reshapeGalleryPanel(row_count); +} + +void LLOutfitGallery::reshapeGalleryPanel(int row_count) +{ + int bottom = 0; + int left = 0; + int height = row_count * (mRowPanelHeight + mVerticalGap); + LLRect rect = LLRect(left, bottom + height, left + mGalleryWidth, bottom); + mGalleryPanel->setRect(rect); + mGalleryPanel->reshape(mGalleryWidth, height); + mGalleryPanel->setVisible(true); + mGalleryPanel->setFollowsLeft(); + mGalleryPanel->setFollowsTop(); +} + +LLPanel* LLOutfitGallery::buildItemPanel(int left) +{ + LLPanel::Params lpparams; + int top = 0; + LLPanel* lpanel = LLUICtrlFactory::create<LLPanel>(lpparams); + LLRect rect = LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top); + lpanel->setRect(rect); + lpanel->reshape(mItemWidth + mItemHorizontalGap, mItemHeight); + lpanel->setVisible(true); + lpanel->setFollowsLeft(); + lpanel->setFollowsTop(); + return lpanel; +} + +LLPanel* LLOutfitGallery::buildRowPanel(int left, int bottom) +{ + LLPanel::Params sparams; + LLPanel* stack = LLUICtrlFactory::create<LLPanel>(sparams); + moveRowPanel(stack, left, bottom); + return stack; +} + +void LLOutfitGallery::moveRowPanel(LLPanel* stack, int left, int bottom) +{ + LLRect rect = LLRect(left, bottom + mRowPanelHeight, left + mRowPanelWidth, bottom); + stack->setRect(rect); + stack->reshape(mRowPanelWidth, mRowPanelHeight); + stack->setVisible(true); + stack->setFollowsLeft(); + stack->setFollowsTop(); +} + +LLOutfitGallery::~LLOutfitGallery() +{ + if (gInventory.containsObserver(mTexturesObserver)) + { + gInventory.removeObserver(mTexturesObserver); + } + delete mTexturesObserver; + + if (gInventory.containsObserver(mOutfitsObserver)) + { + gInventory.removeObserver(mOutfitsObserver); + } + delete mOutfitsObserver; +} + +void LLOutfitGallery::setFilterSubString(const std::string& string) +{ + //TODO: Implement filtering + + sFilterSubString = string; +} + +void LLOutfitGallery::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id) +{ + if (mOutfitMap[base_id]) + { + mOutfitMap[base_id]->setOutfitWorn(true); + } + if (mOutfitMap[prev_id]) + { + mOutfitMap[prev_id]->setOutfitWorn(false); + } +} + +void LLOutfitGallery::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) +{ +} + +void LLOutfitGallery::getCurrentCategories(uuid_vec_t& vcur) +{ + for (outfit_map_t::const_iterator iter = mOutfitMap.begin(); + iter != mOutfitMap.end(); + iter++) + { + if ((*iter).second != NULL) + { + vcur.push_back((*iter).first); + } + } +} + +void LLOutfitGallery::updateAddedCategory(LLUUID cat_id) +{ + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + if (!cat) return; + + std::string name = cat->getName(); + LLOutfitGalleryItem* item = buildGalleryItem(name); + mOutfitMap.insert(LLOutfitGallery::outfit_map_value_t(cat_id, item)); + item->setRightMouseDownCallback(boost::bind(&LLOutfitListBase::outfitRightClickCallBack, this, + _1, _2, _3, cat_id)); + LLWearableItemsList* list = NULL; + item->setFocusReceivedCallback(boost::bind(&LLOutfitListBase::ChangeOutfitSelection, this, list, cat_id)); + if (mGalleryCreated) + { + addToGallery(item); + } + + LLViewerInventoryCategory* outfit_category = gInventory.getCategory(cat_id); + if (!outfit_category) + return; + + if (mOutfitsObserver == NULL) + { + mOutfitsObserver = new LLInventoryCategoriesObserver(); + gInventory.addObserver(mOutfitsObserver); + } + + // Start observing changes in "My Outfits" category. + mOutfitsObserver->addCategory(cat_id, + boost::bind(&LLOutfitGallery::refreshOutfit, this, cat_id)); + + outfit_category->fetch(); + refreshOutfit(cat_id); +} + +void LLOutfitGallery::updateRemovedCategory(LLUUID cat_id) +{ + outfit_map_t::iterator outfits_iter = mOutfitMap.find(cat_id); + if (outfits_iter != mOutfitMap.end()) + { + // 0. Remove category from observer. + mOutfitsObserver->removeCategory(cat_id); + + //const LLUUID& outfit_id = outfits_iter->first; + LLOutfitGalleryItem* item = outfits_iter->second; + + // An outfit is removed from the list. Do the following: + // 2. Remove the outfit from selection. + deselectOutfit(cat_id); + + // 3. Remove category UUID to accordion tab mapping. + mOutfitMap.erase(outfits_iter); + + // 4. Remove outfit from gallery. + removeFromGalleryMiddle(item); + + // kill removed item + if (item != NULL) + { + item->die(); + } + } + +} + +void LLOutfitGallery::updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name) +{ + outfit_map_t::iterator outfit_iter = mOutfitMap.find(cat->getUUID()); + if (outfit_iter != mOutfitMap.end()) + { + // Update name of outfit in gallery + LLOutfitGalleryItem* item = outfit_iter->second; + if (item) + { + item->setOutfitName(name); + } + } +} + +void LLOutfitGallery::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) +{ + if (mOutfitMenu && cat_id.notNull()) + { + uuid_vec_t selected_uuids; + selected_uuids.push_back(cat_id); + mOutfitMenu->show(ctrl, selected_uuids, x, y); + } +} + +void LLOutfitGallery::onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) +{ + if (mSelectedOutfitUUID == category_id) + return; + if (mOutfitMap[mSelectedOutfitUUID]) + { + mOutfitMap[mSelectedOutfitUUID]->setSelected(FALSE); + } + if (mOutfitMap[category_id]) + { + mOutfitMap[category_id]->setSelected(TRUE); + } +} + +bool LLOutfitGallery::hasItemSelected() +{ + return false; +} + +bool LLOutfitGallery::canWearSelected() +{ + return false; +} + +LLOutfitListGearMenuBase* LLOutfitGallery::createGearMenu() +{ + return new LLOutfitGalleryGearMenu(this); +} + +static LLDefaultChildRegistry::Register<LLOutfitGalleryItem> r("outfit_gallery_item"); + +LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p) + : LLPanel(p), + mTexturep(NULL), + mSelected(false), + mWorn(false) +{ + buildFromFile("panel_outfit_gallery_item.xml"); +} + +LLOutfitGalleryItem::~LLOutfitGalleryItem() +{ + +} + +BOOL LLOutfitGalleryItem::postBuild() +{ + setDefaultImage(); + + mOutfitNameText = getChild<LLTextBox>("outfit_name"); + mOutfitWornText = getChild<LLTextBox>("outfit_worn_text"); + mFotoBgPanel = getChild<LLPanel>("foto_bg_panel"); + mTextBgPanel = getChild<LLPanel>("text_bg_panel"); + setOutfitWorn(false); + return TRUE; +} + +void LLOutfitGalleryItem::draw() +{ + LLPanel::draw(); + + // Draw border + LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "OutfitGalleryItemSelected" : "OutfitGalleryItemUnselected", LLColor4::white); + LLRect border = getChildView("preview_outfit")->getRect(); + border.mRight = border.mRight + 1; + gl_rect_2d(border, border_color.get(), FALSE); + + // If the floater is focused, don't apply its alpha to the texture (STORM-677). + const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + if (mTexturep) + { + LLRect interior = border; + interior.stretch(-1); + + gl_draw_scaled_image(interior.mLeft - 1, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); + + // Pump the priority + mTexturep->addTextureStats((F32)(interior.getWidth() * interior.getHeight())); + } + +} + +void LLOutfitGalleryItem::setOutfitName(std::string name) +{ + mOutfitNameText->setText(name); +} + +void LLOutfitGalleryItem::setOutfitWorn(bool value) +{ + mWorn = value; + LLStringUtil::format_map_t worn_string_args; + std::string worn_string = getString("worn_string", worn_string_args); + LLUIColor text_color = LLUIColorTable::instance().getColor(mSelected ? "White" : (mWorn ? "OutfitGalleryItemWorn" : "White"), LLColor4::white); + mOutfitWornText->setReadOnlyColor(text_color.get()); + mOutfitNameText->setReadOnlyColor(text_color.get()); + mOutfitWornText->setValue(value ? worn_string : ""); +} + +void LLOutfitGalleryItem::setSelected(bool value) +{ + mSelected = value; + mTextBgPanel->setBackgroundVisible(value); + setOutfitWorn(mWorn); +} + +BOOL LLOutfitGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask) +{ + setFocus(TRUE); + return LLUICtrl::handleMouseDown(x, y, mask); +} + +void LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id) +{ + mTexturep = LLViewerTextureManager::getFetchedTexture(image_asset_id); + mTexturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW); +} + +void LLOutfitGalleryItem::setDefaultImage() +{ + mTexturep = NULL; +} + +LLOutfitGalleryGearMenu::LLOutfitGalleryGearMenu(LLOutfitListBase* olist) + : LLOutfitListGearMenuBase(olist) +{ +} + +void LLOutfitGalleryGearMenu::onUpdateItemsVisibility() +{ + if (!mMenu) return; + mMenu->setItemVisible("expand", FALSE); + mMenu->setItemVisible("collapse", FALSE); + mMenu->setItemVisible("upload_photo", TRUE); + mMenu->setItemVisible("load_assets", TRUE); + LLOutfitListGearMenuBase::onUpdateItemsVisibility(); +} + +void LLOutfitGalleryGearMenu::onUploadFoto() +{ + LLUUID selected_outfit_id = getSelectedOutfitID(); + LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); + if (gallery && selected_outfit_id.notNull()) + { + gallery->uploadPhoto(selected_outfit_id); + } +} + +void LLOutfitGallery::loadPhotos() +{ + //Iterate over inventory + LLUUID textures = gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE); + LLViewerInventoryCategory* textures_category = gInventory.getCategory(textures); + if (!textures_category) + return; + if (mTexturesObserver == NULL) + { + mTexturesObserver = new LLInventoryCategoriesObserver(); + gInventory.addObserver(mTexturesObserver); + } + + // Start observing changes in "Textures" category. + mTexturesObserver->addCategory(textures, + boost::bind(&LLOutfitGallery::refreshTextures, this, textures)); + + textures_category->fetch(); +} + +void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) +{ + LLViewerInventoryCategory* category = gInventory.getCategory(category_id); + { + bool photo_loaded = false; + LLInventoryModel::cat_array_t sub_cat_array; + LLInventoryModel::item_array_t outfit_item_array; + // Collect all sub-categories of a given category. + gInventory.collectDescendents( + category->getUUID(), + sub_cat_array, + outfit_item_array, + LLInventoryModel::EXCLUDE_TRASH); + BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array) + { + LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem(); + if (linked_item->getActualType() == LLAssetType::AT_TEXTURE) + { + LLUUID asset_id = linked_item->getAssetUUID(); + mOutfitMap[category_id]->setImageAssetId(asset_id); + photo_loaded = true; + break; + } + if (!photo_loaded) + { + mOutfitMap[category_id]->setDefaultImage(); + } + } + } +} + +void LLOutfitGallery::refreshTextures(const LLUUID& category_id) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + // Collect all sub-categories of a given category. + LLIsType is_texture(LLAssetType::AT_TEXTURE); + gInventory.collectDescendentsIf( + category_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_texture); + + //Find texture which contain outfit ID string in name + LLViewerInventoryItem* photo_upload_item = NULL; + BOOST_FOREACH(LLViewerInventoryItem* item, item_array) + { + std::string name = item->getName(); + if (!mOutfitLinkPending.isNull() && name == mOutfitLinkPending.asString()) + { + photo_upload_item = item; + break; + } + } + + if (photo_upload_item != NULL) + { + LLUUID upload_pending_id = photo_upload_item->getUUID(); + LLInventoryObject* upload_object = gInventory.getObject(upload_pending_id); + if (!upload_object) + { + LL_WARNS() << "LLOutfitGallery::refreshTextures added_object is null!" << LL_ENDL; + } + else + { + LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitLinkPending); + linkPhotoToOutfit(upload_pending_id, mOutfitLinkPending); + + LLStringUtil::format_map_t photo_string_args; + photo_string_args["OUTFIT_NAME"] = outfit_cat->getName(); + std::string new_name = getString("outfit_photo_string", photo_string_args); + + LLSD updates; + updates["name"] = new_name; + update_inventory_item(upload_pending_id, updates, NULL); + + } + mOutfitLinkPending.setNull(); + } +} + +void LLOutfitGallery::uploadPhoto(LLUUID outfit_id) +{ + outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id); + if (outfit_it == mOutfitMap.end() || outfit_it->first.isNull()) + { + return; + } + + LLFilePicker& picker = LLFilePicker::instance(); + if (picker.getOpenFile(LLFilePicker::FFLOAD_IMAGE)) + { + std::string filename = picker.getFirstFile(); + LLLocalBitmap* unit = new LLLocalBitmap(filename); + if (unit->getValid()) + { + S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass). + void *nruserdata = NULL; + nruserdata = (void *)&outfit_id; + + LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(outfit_id); + if (!outfit_cat) return; + + checkRemovePhoto(outfit_id); + std::string upload_pending_name = outfit_id.asString(); + LLAssetStorage::LLStoreAssetCallback callback = NULL; + LLUUID photo_id = upload_new_resource(filename, // file + upload_pending_name, + outfit_id.asString(), + 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + upload_pending_name, callback, expected_upload_cost, nruserdata); + mOutfitLinkPending = outfit_id; + } + } +} + +void LLOutfitGallery::linkPhotoToOutfit(LLUUID photo_id, LLUUID outfit_id) +{ + LLPointer<LLInventoryCallback> cb = new LLUpdateGalleryOnPhotoUpload(); + link_inventory_object(outfit_id, photo_id, cb); +} + +bool LLOutfitGallery::checkRemovePhoto(LLUUID outfit_id) +{ + //remove existing photo of outfit from inventory + texture_map_t::iterator texture_it = mTextureMap.find(outfit_id); + if (texture_it != mTextureMap.end()) { + gInventory.removeItem(texture_it->second->getUUID()); + return true; + } + return false; +} + +void LLOutfitGallery::computeDifferenceOfTextures( + const LLInventoryModel::item_array_t& vtextures, + uuid_vec_t& vadded, + uuid_vec_t& vremoved) +{ + uuid_vec_t vnew; + // Creating a vector of newly collected texture UUIDs. + for (LLInventoryModel::item_array_t::const_iterator iter = vtextures.begin(); + iter != vtextures.end(); + iter++) + { + vnew.push_back((*iter)->getUUID()); + } + + uuid_vec_t vcur; + // Creating a vector of currently uploaded texture UUIDs. + for (texture_map_t::const_iterator iter = mTextureMap.begin(); + iter != mTextureMap.end(); + iter++) + { + vcur.push_back((*iter).second->getUUID()); + } + + LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); +} diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h new file mode 100644 index 0000000000..97b80acd19 --- /dev/null +++ b/indra/newview/lloutfitgallery.h @@ -0,0 +1,223 @@ +/** + * @file lloutfitgallery.h + * @author Pavlo Kryvych + * @brief Visual gallery of agent's outfits for My Appearance side panel + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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$ + */ + +#ifndef LL_LLOUTFITGALLERYCTRL_H +#define LL_LLOUTFITGALLERYCTRL_H + +#include "llextendedstatus.h" +#include "lliconctrl.h" +#include "lllayoutstack.h" +#include "lloutfitslist.h" +#include "llpanelappearancetab.h" +#include "llviewertexture.h" + +#include <vector> + +class LLVFS; +class LLOutfitGalleryItem; +class LLOutfitListGearMenuBase; +class LLOutfitGalleryGearMenu; + +class LLUpdateGalleryOnPhotoUpload : public LLInventoryCallback +{ +public: + LLUpdateGalleryOnPhotoUpload(){} + virtual ~LLUpdateGalleryOnPhotoUpload(){} + /* virtual */ void fire(const LLUUID& inv_item){} +private: +}; + + +class LLOutfitGallery : public LLOutfitListBase +{ +public: + friend class LLOutfitGalleryGearMenu; + + struct Params + : public LLInitParam::Block<Params, LLPanel::Params> + { + Optional<S32> row_panel_height; + Optional<S32> row_panel_width_factor; + Optional<S32> gallery_width_factor; + Optional<S32> vertical_gap; + Optional<S32> horizontal_gap; + Optional<S32> item_width; + Optional<S32> item_height; + Optional<S32> item_horizontal_gap; + Optional<S32> items_in_row; + + Params(); + }; + + static const LLOutfitGallery::Params& getDefaultParams(); + + LLOutfitGallery(const LLOutfitGallery::Params& params = getDefaultParams()); + +// LLOutfitGallery(); + virtual ~LLOutfitGallery(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& info); + + /*virtual*/ void setFilterSubString(const std::string& string); + + /*virtual*/ void getCurrentCategories(uuid_vec_t& vcur); + /*virtual*/ void updateAddedCategory(LLUUID cat_id); + /*virtual*/ void updateRemovedCategory(LLUUID cat_id); + /*virtual*/ void updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name); + + /*virtual*/ bool hasItemSelected(); + /*virtual*/ bool canWearSelected(); + + /*virtual*/ bool getHasExpandableFolders() { return FALSE; } + + void refreshTextures(const LLUUID& category_id); + void refreshOutfit(const LLUUID& category_id); + void computeDifferenceOfTextures(const LLInventoryModel::item_array_t& vtextures, uuid_vec_t& vadded, uuid_vec_t& vremoved); + +protected: + /*virtual*/ void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id); + /*virtual*/ void onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid); + /*virtual*/ void onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id); + /*virtual*/ void onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id); + + /*virtual*/ void onCollapseAllFolders() {} + /*virtual*/ void onExpandAllFolders() {} + /*virtual*/ LLOutfitListGearMenuBase* createGearMenu(); + +private: + void loadPhotos(); + void uploadPhoto(LLUUID outfit_id); + void linkPhotoToOutfit(LLUUID outfit_id, LLUUID photo_id); + bool checkRemovePhoto(LLUUID outfit_id); + void addToGallery(LLOutfitGalleryItem* item); + void removeFromGalleryLast(LLOutfitGalleryItem* item); + void removeFromGalleryMiddle(LLOutfitGalleryItem* item); + LLPanel* addLastRow(); + void removeLastRow(); + void moveRowUp(int row); + void moveRowDown(int row); + void moveRow(int row, int pos); + LLPanel* addToRow(LLPanel* row_stack, LLOutfitGalleryItem* item, int pos, int hgap); + void removeFromLastRow(LLOutfitGalleryItem* item); + + LLOutfitGalleryItem* buildGalleryItem(std::string name); + void buildGalleryPanel(int row_count); + void reshapeGalleryPanel(int row_count); + LLPanel* buildItemPanel(int left); + LLPanel* buildRowPanel(int left, int bottom); + void moveRowPanel(LLPanel* stack, int left, int bottom); + std::vector<LLPanel*> mRowPanels; + std::vector<LLPanel*> mItemPanels; + std::vector<LLOutfitGalleryItem*> mItems; + LLScrollContainer* mScrollPanel; + LLPanel* mGalleryPanel; + LLPanel* mLastRowPanel; + LLUUID mOutfitLinkPending; + bool mGalleryCreated; + int mRowCount; + int mItemsAddedCount; + /* Params */ + int mRowPanelHeight; + int mVerticalGap; + int mHorizontalGap; + int mItemWidth; + int mItemHeight; + int mItemHorizontalGap; + int mItemsInRow; + int mRowPanelWidth; + int mGalleryWidth; + + /* + #define LAYOUT_STACK_WIDTH_FACTOR 166 + #define LAYOUT_STACK_WIDTH LAYOUT_STACK_WIDTH_FACTOR * ITEMS_IN_ROW//498 + #define GALLERY_WIDTH_FACTOR 163 + #define GALLERY_WIDTH GALLERY_WIDTH_FACTOR * ITEMS_IN_ROW//485//290 + */ + + /* + #define GALLERY_ITEM_HGAP 16 + #define ITEMS_IN_ROW 3 + */ + + typedef std::map<LLUUID, LLOutfitGalleryItem*> outfit_map_t; + typedef outfit_map_t::value_type outfit_map_value_t; + outfit_map_t mOutfitMap; + typedef std::map<LLUUID, LLViewerInventoryItem*> texture_map_t; + typedef texture_map_t::value_type texture_map_value_t; + texture_map_t mTextureMap; + typedef std::map<LLOutfitGalleryItem*, int> item_num_map_t; + typedef item_num_map_t::value_type item_numb_map_value_t; + item_num_map_t mItemIndexMap; + + + LLInventoryCategoriesObserver* mTexturesObserver; + LLInventoryCategoriesObserver* mOutfitsObserver; +}; + +class LLOutfitGalleryGearMenu : public LLOutfitListGearMenuBase +{ +public: + friend class LLOutfitGallery; + LLOutfitGalleryGearMenu(LLOutfitListBase* olist); + +protected: + /*virtual*/ void onUpdateItemsVisibility(); + +private: + /*virtual*/ void onUploadFoto(); +}; + +class LLOutfitGalleryItem : public LLPanel +{ +public: + struct Params : public LLInitParam::Block<Params, LLPanel::Params> + {}; + + LLOutfitGalleryItem(const Params& p); + virtual ~LLOutfitGalleryItem(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + + void setDefaultImage(); + void setImageAssetId(LLUUID asset_id); + void setOutfitName(std::string name); + void setOutfitWorn(bool value); + void setSelected(bool value); +private: + LLPointer<LLViewerTexture> mTexturep; + LLTextBox* mOutfitNameText; + LLTextBox* mOutfitWornText; + LLPanel* mTextBgPanel; + LLPanel* mFotoBgPanel; + bool mSelected; + bool mWorn; +}; + +#endif // LL_LLOUTFITGALLERYCTRL_H diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 883221382c..a7636fc368 100755 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -38,7 +38,6 @@ #include "llfloatersidepanelcontainer.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" -#include "lllistcontextmenu.h" #include "llmenubutton.h" #include "llnotificationsutil.h" #include "lloutfitobserver.h" @@ -98,276 +97,18 @@ const outfit_accordion_tab_params& get_accordion_tab_params() } -////////////////////////////////////////////////////////////////////////// - -class LLOutfitListGearMenu -{ -public: - LLOutfitListGearMenu(LLOutfitsList* olist) - : mOutfitList(olist), - mMenu(NULL) - { - llassert_always(mOutfitList); - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenu::onWear, this)); - registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenu::onTakeOff, this)); - registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenu::onRename, this)); - registrar.add("Gear.Delete", boost::bind(&LLOutfitsList::removeSelected, mOutfitList)); - registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenu::onCreate, this, _2)); - registrar.add("Gear.Collapse", boost::bind(&LLOutfitsList::collapse_all_folders, mOutfitList)); - registrar.add("Gear.Expand", boost::bind(&LLOutfitsList::expand_all_folders, mOutfitList)); - - registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenu::onAdd, this)); - - enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenu::onEnable, this, _2)); - enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenu::onVisible, this, _2)); - - mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( - "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - llassert(mMenu); - } - - void updateItemsVisibility() - { - if (!mMenu) return; - - bool have_selection = getSelectedOutfitID().notNull(); - mMenu->setItemVisible("sepatator1", have_selection); - mMenu->setItemVisible("sepatator2", have_selection); - mMenu->arrangeAndClear(); // update menu height - } - - LLToggleableMenu* getMenu() { return mMenu; } - -private: - const LLUUID& getSelectedOutfitID() - { - return mOutfitList->getSelectedOutfitUUID(); - } - - LLViewerInventoryCategory* getSelectedOutfit() - { - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.isNull()) - { - return NULL; - } - - LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); - return cat; - } - - void onWear() - { - LLViewerInventoryCategory* selected_outfit = getSelectedOutfit(); - if (selected_outfit) - { - LLAppearanceMgr::instance().wearInventoryCategory( - selected_outfit, /*copy=*/ FALSE, /*append=*/ FALSE); - } - } - - void onAdd() - { - const LLUUID& selected_id = getSelectedOutfitID(); - - if (selected_id.notNull()) - { - LLAppearanceMgr::getInstance()->addCategoryToCurrentOutfit(selected_id); - } - } - - void onTakeOff() - { - // Take off selected outfit. - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.notNull()) - { - LLAppearanceMgr::instance().takeOffOutfit(selected_outfit_id); - } - } - - void onRename() - { - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.notNull()) - { - LLAppearanceMgr::instance().renameOutfit(selected_outfit_id); - } - } - - void onCreate(const LLSD& data) - { - LLWearableType::EType type = LLWearableType::typeNameToType(data.asString()); - if (type == LLWearableType::WT_NONE) - { - LL_WARNS() << "Invalid wearable type" << LL_ENDL; - return; - } - - LLAgentWearables::createWearable(type, true); - } - - bool onEnable(LLSD::String param) - { - // Handle the "Wear - Replace Current Outfit" menu option specially - // because LLOutfitList::isActionEnabled() checks whether it's allowed - // to wear selected outfit OR selected items, while we're only - // interested in the outfit (STORM-183). - if ("wear" == param) - { - return LLAppearanceMgr::instance().getCanReplaceCOF(mOutfitList->getSelectedOutfitUUID()); - } - - return mOutfitList->isActionEnabled(param); - } - - bool onVisible(LLSD::String param) - { - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.isNull()) // no selection or invalid outfit selected - { - return false; - } - - // *TODO This condition leads to menu item behavior inconsistent with - // "Wear" button behavior and should be modified or removed. - bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == selected_outfit_id; - - if ("wear" == param) - { - return !is_worn; - } - - return true; - } - - LLOutfitsList* mOutfitList; - LLToggleableMenu* mMenu; -}; - -////////////////////////////////////////////////////////////////////////// - -class LLOutfitContextMenu : public LLListContextMenu -{ -public: - - LLOutfitContextMenu(LLOutfitsList* outfit_list) - : LLListContextMenu(), - mOutfitList(outfit_list) - {} -protected: - /* virtual */ LLContextMenu* createMenu() - { - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - LLUUID selected_id = mUUIDs.front(); - - registrar.add("Outfit.WearReplace", - boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); - registrar.add("Outfit.WearAdd", - boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); - registrar.add("Outfit.TakeOff", - boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); - registrar.add("Outfit.Edit", boost::bind(editOutfit)); - registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); - registrar.add("Outfit.Delete", boost::bind(&LLOutfitsList::removeSelected, mOutfitList)); - - enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2)); - enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitContextMenu::onVisible, this, _2)); - - return createFromFile("menu_outfit_tab.xml"); - } - - bool onEnable(LLSD::String param) - { - LLUUID outfit_cat_id = mUUIDs.back(); - - if ("rename" == param) - { - return get_is_category_renameable(&gInventory, outfit_cat_id); - } - else if ("wear_replace" == param) - { - return LLAppearanceMgr::instance().getCanReplaceCOF(outfit_cat_id); - } - else if ("wear_add" == param) - { - return LLAppearanceMgr::getCanAddToCOF(outfit_cat_id); - } - else if ("take_off" == param) - { - return LLAppearanceMgr::getCanRemoveFromCOF(outfit_cat_id); - } - - return true; - } - - bool onVisible(LLSD::String param) - { - LLUUID outfit_cat_id = mUUIDs.back(); - bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id; - - if ("edit" == param) - { - return is_worn; - } - else if ("wear_replace" == param) - { - return !is_worn; - } - else if ("delete" == param) - { - return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id); - } - - return true; - } - - static void editOutfit() - { - LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit")); - } - - static void renameOutfit(const LLUUID& outfit_cat_id) - { - LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); - } - -private: - LLOutfitsList* mOutfitList; -}; - -////////////////////////////////////////////////////////////////////////// - static LLPanelInjector<LLOutfitsList> t_outfits_list("outfits_list"); LLOutfitsList::LLOutfitsList() - : LLPanelAppearanceTab() - , mAccordion(NULL) + : LLOutfitListBase() + , mAccordion(NULL) , mListCommands(NULL) - , mIsInitialized(false) , mItemSelected(false) { - mCategoriesObserver = new LLInventoryCategoriesObserver(); - - mGearMenu = new LLOutfitListGearMenu(this); - mOutfitMenu = new LLOutfitContextMenu(this); } LLOutfitsList::~LLOutfitsList() { - delete mGearMenu; - delete mOutfitMenu; - - if (gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - } - delete mCategoriesObserver; } BOOL LLOutfitsList::postBuild() @@ -375,54 +116,20 @@ BOOL LLOutfitsList::postBuild() mAccordion = getChild<LLAccordionCtrl>("outfits_accordion"); mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); - LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn"); - - menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenu::updateItemsVisibility, mGearMenu)); - menu_gear_btn->setMenu(mGearMenu->getMenu()); - - return TRUE; + return LLOutfitListBase::postBuild(); } //virtual -void LLOutfitsList::onOpen(const LLSD& /*info*/) +void LLOutfitsList::onOpen(const LLSD& info) { - if (!mIsInitialized) - { - // *TODO: I'm not sure is this check necessary but it never match while developing. - if (!gInventory.isInventoryUsable()) - return; - - const LLUUID outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - - // *TODO: I'm not sure is this check necessary but it never match while developing. - LLViewerInventoryCategory* category = gInventory.getCategory(outfits); - if (!category) - return; - - gInventory.addObserver(mCategoriesObserver); - - // Start observing changes in "My Outfits" category. - mCategoriesObserver->addCategory(outfits, - boost::bind(&LLOutfitsList::refreshList, this, outfits)); - - const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); - - // Start observing changes in Current Outfit category. - mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); + if (!mIsInitialized) + { + const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + // Start observing changes in Current Outfit category. + mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); + } - LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(&LLOutfitsList::highlightBaseOutfit, this)); - LLOutfitObserver::instance().addBOFReplacedCallback(boost::bind(&LLOutfitsList::highlightBaseOutfit, this)); - - // Fetch "My Outfits" contents and refresh the list to display - // initially fetched items. If not all items are fetched now - // the observer will refresh the list as soon as the new items - // arrive. - category->fetch(); - refreshList(outfits); - highlightBaseOutfit(); - - mIsInitialized = true; - } + LLOutfitListBase::onOpen(info); LLAccordionCtrlTab* selected_tab = mAccordion->getSelectedTab(); if (!selected_tab) return; @@ -431,174 +138,131 @@ void LLOutfitsList::onOpen(const LLSD& /*info*/) selected_tab->showAndFocusHeader(); } -void LLOutfitsList::refreshList(const LLUUID& category_id) -{ - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - // Collect all sub-categories of a given category. - LLIsType is_category(LLAssetType::AT_CATEGORY); - gInventory.collectDescendentsIf( - category_id, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_category); - - uuid_vec_t vadded; - uuid_vec_t vremoved; - - // Create added and removed items vectors. - computeDifference(cat_array, vadded, vremoved); - - // Handle added tabs. - for (uuid_vec_t::const_iterator iter = vadded.begin(); - iter != vadded.end(); - ++iter) - { - const LLUUID cat_id = (*iter); - LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); - if (!cat) continue; - - std::string name = cat->getName(); - - outfit_accordion_tab_params tab_params(get_accordion_tab_params()); - LLAccordionCtrlTab* tab = LLUICtrlFactory::create<LLAccordionCtrlTab>(tab_params); - if (!tab) continue; - LLWearableItemsList* wearable_list = LLUICtrlFactory::create<LLWearableItemsList>(tab_params.wearable_list); - wearable_list->setShape(tab->getLocalRect()); - tab->addChild(wearable_list); - - tab->setName(name); - tab->setTitle(name); - - // *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated. - tab->setDisplayChildren(false); - mAccordion->addCollapsibleCtrl(tab); - - // Start observing the new outfit category. - LLWearableItemsList* list = tab->getChild<LLWearableItemsList>("wearable_items_list"); - if (!mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id))) - { - // Remove accordion tab if category could not be added to observer. - mAccordion->removeCollapsibleCtrl(tab); - - // kill removed tab - tab->die(); - continue; - } +void LLOutfitsList::updateAddedCategory(LLUUID cat_id) +{ + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + if (!cat) return; - // Map the new tab with outfit category UUID. - mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab)); + std::string name = cat->getName(); - tab->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onAccordionTabRightClick, this, - _1, _2, _3, cat_id)); + outfit_accordion_tab_params tab_params(get_accordion_tab_params()); + LLAccordionCtrlTab* tab = LLUICtrlFactory::create<LLAccordionCtrlTab>(tab_params); + if (!tab) return; + LLWearableItemsList* wearable_list = LLUICtrlFactory::create<LLWearableItemsList>(tab_params.wearable_list); + wearable_list->setShape(tab->getLocalRect()); + tab->addChild(wearable_list); - // Setting tab focus callback to monitor currently selected outfit. - tab->setFocusReceivedCallback(boost::bind(&LLOutfitsList::changeOutfitSelection, this, list, cat_id)); + tab->setName(name); + tab->setTitle(name); - // Setting callback to reset items selection inside outfit on accordion collapsing and expanding (EXT-7875) - tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::resetItemSelection, this, list, cat_id)); + // *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated. + tab->setDisplayChildren(false); + mAccordion->addCollapsibleCtrl(tab); - // force showing list items that don't match current filter(EXT-7158) - list->setForceShowingUnmatchedItems(true); + // Start observing the new outfit category. + LLWearableItemsList* list = tab->getChild<LLWearableItemsList>("wearable_items_list"); + if (!mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id))) + { + // Remove accordion tab if category could not be added to observer. + mAccordion->removeCollapsibleCtrl(tab); - // Setting list commit callback to monitor currently selected wearable item. - list->setCommitCallback(boost::bind(&LLOutfitsList::onSelectionChange, this, _1)); + // kill removed tab + tab->die(); + return; + } - // Setting list refresh callback to apply filter on list change. - list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onFilteredWearableItemsListRefresh, this, _1)); + // Map the new tab with outfit category UUID. + mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab)); - list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3)); + tab->setRightMouseDownCallback(boost::bind(&LLOutfitListBase::outfitRightClickCallBack, this, + _1, _2, _3, cat_id)); - // Fetch the new outfit contents. - cat->fetch(); + // Setting tab focus callback to monitor currently selected outfit. + tab->setFocusReceivedCallback(boost::bind(&LLOutfitListBase::ChangeOutfitSelection, this, list, cat_id)); - // Refresh the list of outfit items after fetch(). - // Further list updates will be triggered by the category observer. - list->updateList(cat_id); + // Setting callback to reset items selection inside outfit on accordion collapsing and expanding (EXT-7875) + tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::resetItemSelection, this, list, cat_id)); - // If filter is currently applied we store the initial tab state and - // open it to show matched items if any. - if (!sFilterSubString.empty()) - { - tab->notifyChildren(LLSD().with("action","store_state")); - tab->setDisplayChildren(true); + // force showing list items that don't match current filter(EXT-7158) + list->setForceShowingUnmatchedItems(true); - // Setting mForceRefresh flag will make the list refresh its contents - // even if it is not currently visible. This is required to apply the - // filter to the newly added list. - list->setForceRefresh(true); + // Setting list commit callback to monitor currently selected wearable item. + list->setCommitCallback(boost::bind(&LLOutfitsList::onListSelectionChange, this, _1)); - list->setFilterSubString(sFilterSubString); - } - } + // Setting list refresh callback to apply filter on list change. + list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onFilteredWearableItemsListRefresh, this, _1)); - // Handle removed tabs. - for (uuid_vec_t::const_iterator iter=vremoved.begin(); iter != vremoved.end(); ++iter) - { - outfits_map_t::iterator outfits_iter = mOutfitsMap.find((*iter)); - if (outfits_iter != mOutfitsMap.end()) - { - const LLUUID& outfit_id = outfits_iter->first; - LLAccordionCtrlTab* tab = outfits_iter->second; + list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3)); - // An outfit is removed from the list. Do the following: - // 1. Remove outfit category from observer to stop monitoring its changes. - mCategoriesObserver->removeCategory(outfit_id); + // Fetch the new outfit contents. + cat->fetch(); - // 2. Remove the outfit from selection. - deselectOutfit(outfit_id); + // Refresh the list of outfit items after fetch(). + // Further list updates will be triggered by the category observer. + list->updateList(cat_id); - // 3. Remove category UUID to accordion tab mapping. - mOutfitsMap.erase(outfits_iter); + // If filter is currently applied we store the initial tab state and + // open it to show matched items if any. + if (!sFilterSubString.empty()) + { + tab->notifyChildren(LLSD().with("action", "store_state")); + tab->setDisplayChildren(true); - // 4. Remove outfit tab from accordion. - mAccordion->removeCollapsibleCtrl(tab); + // Setting mForceRefresh flag will make the list refresh its contents + // even if it is not currently visible. This is required to apply the + // filter to the newly added list. + list->setForceRefresh(true); - // kill removed tab - if (tab != NULL) - { - tab->die(); - } - } - } - - // Get changed items from inventory model and update outfit tabs - // which might have been renamed. - const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); - for (LLInventoryModel::changed_items_t::const_iterator items_iter = changed_items.begin(); - items_iter != changed_items.end(); - ++items_iter) - { - updateOutfitTab(*items_iter); - } - - mAccordion->sort(); + list->setFilterSubString(sFilterSubString); + } } -void LLOutfitsList::highlightBaseOutfit() +void LLOutfitsList::updateRemovedCategory(LLUUID cat_id) { - // id of base outfit - LLUUID base_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); - if (base_id != mHighlightedOutfitUUID) - { - if (mOutfitsMap[mHighlightedOutfitUUID]) - { - mOutfitsMap[mHighlightedOutfitUUID]->setTitleFontStyle("NORMAL"); - mOutfitsMap[mHighlightedOutfitUUID]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor")); - } + outfits_map_t::iterator outfits_iter = mOutfitsMap.find(cat_id); + if (outfits_iter != mOutfitsMap.end()) + { + const LLUUID& outfit_id = outfits_iter->first; + LLAccordionCtrlTab* tab = outfits_iter->second; + + // An outfit is removed from the list. Do the following: + // 1. Remove outfit category from observer to stop monitoring its changes. + mCategoriesObserver->removeCategory(outfit_id); + + // 2. Remove the outfit from selection. + deselectOutfit(outfit_id); + + // 3. Remove category UUID to accordion tab mapping. + mOutfitsMap.erase(outfits_iter); + + // 4. Remove outfit tab from accordion. + mAccordion->removeCollapsibleCtrl(tab); + + // kill removed tab + if (tab != NULL) + { + tab->die(); + } + } +} - mHighlightedOutfitUUID = base_id; - } - if (mOutfitsMap[base_id]) +//virtual +void LLOutfitsList::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id) +{ + if (mOutfitsMap[prev_id]) + { + mOutfitsMap[prev_id]->setTitleFontStyle("NORMAL"); + mOutfitsMap[prev_id]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor")); + } + if (mOutfitsMap[base_id]) { mOutfitsMap[base_id]->setTitleFontStyle("BOLD"); mOutfitsMap[base_id]->setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor")); } } -void LLOutfitsList::onSelectionChange(LLUICtrl* ctrl) +void LLOutfitsList::onListSelectionChange(LLUICtrl* ctrl) { LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(ctrl); if (!list) return; @@ -606,10 +270,10 @@ void LLOutfitsList::onSelectionChange(LLUICtrl* ctrl) LLViewerInventoryItem *item = gInventory.getItem(list->getSelectedUUID()); if (!item) return; - changeOutfitSelection(list, item->getParentUUID()); + ChangeOutfitSelection(list, item->getParentUUID()); } -void LLOutfitsList::performAction(std::string action) +void LLOutfitListBase::performAction(std::string action) { if (mSelectedOutfitUUID.isNull()) return; @@ -630,23 +294,7 @@ void LLOutfitsList::performAction(std::string action) } } -void LLOutfitsList::removeSelected() -{ - LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitsList::onOutfitsRemovalConfirmation, this, _1, _2)); -} - -void LLOutfitsList::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - - if (mSelectedOutfitUUID.notNull()) - { - gInventory.removeCategory(mSelectedOutfitUUID); - } -} - -void LLOutfitsList::setSelectedOutfitByUUID(const LLUUID& outfit_uuid) +void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) { for (outfits_map_t::iterator iter = mOutfitsMap.begin(); iter != mOutfitsMap.end(); @@ -661,7 +309,7 @@ void LLOutfitsList::setSelectedOutfitByUUID(const LLUUID& outfit_uuid) if (!list) continue; tab->setFocus(TRUE); - changeOutfitSelection(list, outfit_uuid); + ChangeOutfitSelection(list, outfit_uuid); tab->setDisplayChildren(true); } @@ -677,14 +325,14 @@ void LLOutfitsList::setFilterSubString(const std::string& string) } // virtual -bool LLOutfitsList::isActionEnabled(const LLSD& userdata) +bool LLOutfitListBase::isActionEnabled(const LLSD& userdata) { if (mSelectedOutfitUUID.isNull()) return false; const std::string command_name = userdata.asString(); if (command_name == "delete") { - return !mItemSelected && LLAppearanceMgr::instance().getCanRemoveOutfit(mSelectedOutfitUUID); + return !hasItemSelected() && LLAppearanceMgr::instance().getCanRemoveOutfit(mSelectedOutfitUUID); } if (command_name == "rename") { @@ -745,7 +393,7 @@ void LLOutfitsList::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const } } -void LLOutfitsList::collapse_all_folders() +void LLOutfitsList::onCollapseAllFolders() { for (outfits_map_t::iterator iter = mOutfitsMap.begin(); iter != mOutfitsMap.end(); @@ -759,7 +407,7 @@ void LLOutfitsList::collapse_all_folders() } } -void LLOutfitsList::expand_all_folders() +void LLOutfitsList::onExpandAllFolders() { for (outfits_map_t::iterator iter = mOutfitsMap.begin(); iter != mOutfitsMap.end(); @@ -773,11 +421,6 @@ void LLOutfitsList::expand_all_folders() } } -boost::signals2::connection LLOutfitsList::setSelectionChangeCallback(selection_change_callback_t cb) -{ - return mSelectionChangeSignal.connect(cb); -} - bool LLOutfitsList::hasItemSelected() { return mItemSelected; @@ -786,42 +429,12 @@ bool LLOutfitsList::hasItemSelected() ////////////////////////////////////////////////////////////////////////// // Private methods ////////////////////////////////////////////////////////////////////////// -void LLOutfitsList::computeDifference( - const LLInventoryModel::cat_array_t& vcats, - uuid_vec_t& vadded, - uuid_vec_t& vremoved) -{ - uuid_vec_t vnew; - // Creating a vector of newly collected sub-categories UUIDs. - for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin(); - iter != vcats.end(); - iter++) - { - vnew.push_back((*iter)->getUUID()); - } - uuid_vec_t vcur; - // Creating a vector of currently displayed sub-categories UUIDs. - for (outfits_map_t::const_iterator iter = mOutfitsMap.begin(); - iter != mOutfitsMap.end(); - iter++) - { - vcur.push_back((*iter).first); - } - - LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); -} - -void LLOutfitsList::updateOutfitTab(const LLUUID& category_id) +void LLOutfitsList::updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name) { - outfits_map_t::iterator outfits_iter = mOutfitsMap.find(category_id); + outfits_map_t::iterator outfits_iter = mOutfitsMap.find(cat->getUUID()); if (outfits_iter != mOutfitsMap.end()) { - LLViewerInventoryCategory *cat = gInventory.getCategory(category_id); - if (!cat) return; - - std::string name = cat->getName(); - // Update tab name with the new category name. LLAccordionCtrlTab* tab = outfits_iter->second; if (tab) @@ -836,10 +449,10 @@ void LLOutfitsList::resetItemSelection(LLWearableItemsList* list, const LLUUID& { list->resetSelection(); mItemSelected = false; - setSelectedOutfitUUID(category_id); + signalSelectionOutfitUUID(category_id); } -void LLOutfitsList::changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) +void LLOutfitsList::onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) { MASK mask = gKeyboard->currentMask(TRUE); @@ -865,24 +478,14 @@ void LLOutfitsList::changeOutfitSelection(LLWearableItemsList* list, const LLUUI mItemSelected = list && (list->getSelectedItem() != NULL); mSelectedListsMap.insert(wearables_lists_map_value_t(category_id, list)); - setSelectedOutfitUUID(category_id); -} - -void LLOutfitsList::setSelectedOutfitUUID(const LLUUID& category_id) -{ - mSelectionChangeSignal(mSelectedOutfitUUID = category_id); } void LLOutfitsList::deselectOutfit(const LLUUID& category_id) { // Remove selected lists map entry. mSelectedListsMap.erase(category_id); - - // Reset selection if the outfit is selected. - if (category_id == mSelectedOutfitUUID) - { - setSelectedOutfitUUID(LLUUID::null); - } + + LLOutfitListBase::deselectOutfit(category_id); } void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID& category_id) @@ -890,7 +493,7 @@ void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID // Try restoring outfit selection after filtering. if (mAccordion->getSelectedTab() == tab) { - setSelectedOutfitUUID(category_id); + signalSelectionOutfitUUID(category_id); } } @@ -1036,24 +639,6 @@ bool LLOutfitsList::canWearSelected() return true; } -void LLOutfitsList::onAccordionTabRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) -{ - LLAccordionCtrlTab* tab = dynamic_cast<LLAccordionCtrlTab*>(ctrl); - if(mOutfitMenu && is_tab_header_clicked(tab, y) && cat_id.notNull()) - { - // Focus tab header to trigger tab selection change. - LLUICtrl* header = tab->findChild<LLUICtrl>("dd_header"); - if (header) - { - header->setFocus(TRUE); - } - - uuid_vec_t selected_uuids; - selected_uuids.push_back(cat_id); - mOutfitMenu->show(ctrl, selected_uuids, x, y); - } -} - void LLOutfitsList::wearSelectedItems() { uuid_vec_t selected_uuids; @@ -1132,6 +717,47 @@ void LLOutfitsList::onCOFChanged() } } +void LLOutfitsList::getCurrentCategories(uuid_vec_t& vcur) +{ + // Creating a vector of currently displayed sub-categories UUIDs. + for (outfits_map_t::const_iterator iter = mOutfitsMap.begin(); + iter != mOutfitsMap.end(); + iter++) + { + vcur.push_back((*iter).first); + } +} + + +void LLOutfitsList::sortOutfits() +{ + mAccordion->sort(); +} + +void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) +{ + LLAccordionCtrlTab* tab = dynamic_cast<LLAccordionCtrlTab*>(ctrl); + if (mOutfitMenu && is_tab_header_clicked(tab, y) && cat_id.notNull()) + { + // Focus tab header to trigger tab selection change. + LLUICtrl* header = tab->findChild<LLUICtrl>("dd_header"); + if (header) + { + header->setFocus(TRUE); + } + + uuid_vec_t selected_uuids; + selected_uuids.push_back(cat_id); + mOutfitMenu->show(ctrl, selected_uuids, x, y); + } +} + +LLOutfitListGearMenuBase* LLOutfitsList::createGearMenu() +{ + return new LLOutfitListGearMenu(this); +} + + bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y) { if(!tab || !tab->getHeaderVisible()) return false; @@ -1140,4 +766,484 @@ bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y) return y >= header_bottom; } +LLOutfitListBase::LLOutfitListBase() + : LLPanelAppearanceTab() + , mIsInitialized(false) +{ + mCategoriesObserver = new LLInventoryCategoriesObserver(); + mOutfitMenu = new LLOutfitContextMenu(this); + //mGearMenu = createGearMenu(); +} + +LLOutfitListBase::~LLOutfitListBase() +{ + delete mOutfitMenu; + delete mGearMenu; + + if (gInventory.containsObserver(mCategoriesObserver)) + { + gInventory.removeObserver(mCategoriesObserver); + } + delete mCategoriesObserver; +} + +void LLOutfitListBase::onOpen(const LLSD& info) +{ + if (!mIsInitialized) + { + // *TODO: I'm not sure is this check necessary but it never match while developing. + if (!gInventory.isInventoryUsable()) + return; + + const LLUUID outfits = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + // *TODO: I'm not sure is this check necessary but it never match while developing. + LLViewerInventoryCategory* category = gInventory.getCategory(outfits); + if (!category) + return; + + gInventory.addObserver(mCategoriesObserver); + + // Start observing changes in "My Outfits" category. + mCategoriesObserver->addCategory(outfits, + boost::bind(&LLOutfitListBase::refreshList, this, outfits)); + + const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + + // Start observing changes in Current Outfit category. + //mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); + + LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(&LLOutfitListBase::highlightBaseOutfit, this)); + LLOutfitObserver::instance().addBOFReplacedCallback(boost::bind(&LLOutfitListBase::highlightBaseOutfit, this)); + + // Fetch "My Outfits" contents and refresh the list to display + // initially fetched items. If not all items are fetched now + // the observer will refresh the list as soon as the new items + // arrive. + category->fetch(); + refreshList(outfits); + highlightBaseOutfit(); + + mIsInitialized = true; + } +} + +void LLOutfitListBase::refreshList(const LLUUID& category_id) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + // Collect all sub-categories of a given category. + LLIsType is_category(LLAssetType::AT_CATEGORY); + gInventory.collectDescendentsIf( + category_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_category); + + uuid_vec_t vadded; + uuid_vec_t vremoved; + + // Create added and removed items vectors. + computeDifference(cat_array, vadded, vremoved); + + // Handle added tabs. + for (uuid_vec_t::const_iterator iter = vadded.begin(); + iter != vadded.end(); + ++iter) + { + const LLUUID cat_id = (*iter); + updateAddedCategory(cat_id); + } + + // Handle removed tabs. + for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) + { + const LLUUID cat_id = (*iter); + updateRemovedCategory(cat_id); + } + + // Get changed items from inventory model and update outfit tabs + // which might have been renamed. + const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); + for (LLInventoryModel::changed_items_t::const_iterator items_iter = changed_items.begin(); + items_iter != changed_items.end(); + ++items_iter) + { + LLViewerInventoryCategory *cat = gInventory.getCategory(*items_iter); + if (!cat) return; + + std::string name = cat->getName(); + + updateChangedCategoryName(cat, name); + } + + sortOutfits(); +} + +void LLOutfitListBase::computeDifference( + const LLInventoryModel::cat_array_t& vcats, + uuid_vec_t& vadded, + uuid_vec_t& vremoved) +{ + uuid_vec_t vnew; + // Creating a vector of newly collected sub-categories UUIDs. + for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin(); + iter != vcats.end(); + iter++) + { + vnew.push_back((*iter)->getUUID()); + } + + uuid_vec_t vcur; + getCurrentCategories(vcur); + + LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); +} + +void LLOutfitListBase::sortOutfits() +{ +} + +void LLOutfitListBase::highlightBaseOutfit() +{ + // id of base outfit + LLUUID base_id = LLAppearanceMgr::getInstance()->getBaseOutfitUUID(); + if (base_id != mHighlightedOutfitUUID) + { + LLUUID prev_id = mHighlightedOutfitUUID; + mHighlightedOutfitUUID = base_id; + onHighlightBaseOutfit(base_id, prev_id); + } + +} + +void LLOutfitListBase::removeSelected() +{ + LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitListBase::onOutfitsRemovalConfirmation, this, _1, _2)); +} + +void LLOutfitListBase::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + if (mSelectedOutfitUUID.notNull()) + { + gInventory.removeCategory(mSelectedOutfitUUID); + } +} + +void LLOutfitListBase::setSelectedOutfitByUUID(const LLUUID& outfit_uuid) +{ + onSetSelectedOutfitByUUID(outfit_uuid); +} + +boost::signals2::connection LLOutfitListBase::setSelectionChangeCallback(selection_change_callback_t cb) +{ + return mSelectionChangeSignal.connect(cb); +} + +void LLOutfitListBase::signalSelectionOutfitUUID(const LLUUID& category_id) +{ + mSelectionChangeSignal(category_id); +} + +void LLOutfitListBase::outfitRightClickCallBack(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) +{ + onOutfitRightClick(ctrl, x, y, cat_id); +} + +void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) +{ + onChangeOutfitSelection(list, category_id); + mSelectedOutfitUUID = category_id; + signalSelectionOutfitUUID(category_id); +} + +BOOL LLOutfitListBase::postBuild() +{ + mGearMenu = createGearMenu(); + + LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn"); + + menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenuBase::updateItemsVisibility, mGearMenu)); + menu_gear_btn->setMenu(mGearMenu->getMenu()); + return TRUE; +} + +void LLOutfitListBase::collapseAllFolders() +{ + onCollapseAllFolders(); +} + +void LLOutfitListBase::expandAllFolders() +{ + onExpandAllFolders(); +} + +void LLOutfitListBase::deselectOutfit(const LLUUID& category_id) +{ + // Reset selection if the outfit is selected. + if (category_id == mSelectedOutfitUUID) + { + signalSelectionOutfitUUID(LLUUID::null); + } +} + +LLContextMenu* LLOutfitContextMenu::createMenu() +{ + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.front(); + + registrar.add("Outfit.WearReplace", + boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.WearAdd", + boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.TakeOff", + boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.Edit", boost::bind(editOutfit)); + registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); + registrar.add("Outfit.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); + + enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2)); + enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitContextMenu::onVisible, this, _2)); + + return createFromFile("menu_outfit_tab.xml"); + +} + +bool LLOutfitContextMenu::onEnable(LLSD::String param) +{ + LLUUID outfit_cat_id = mUUIDs.back(); + + if ("rename" == param) + { + return get_is_category_renameable(&gInventory, outfit_cat_id); + } + else if ("wear_replace" == param) + { + return LLAppearanceMgr::instance().getCanReplaceCOF(outfit_cat_id); + } + else if ("wear_add" == param) + { + return LLAppearanceMgr::getCanAddToCOF(outfit_cat_id); + } + else if ("take_off" == param) + { + return LLAppearanceMgr::getCanRemoveFromCOF(outfit_cat_id); + } + + return true; +} + +bool LLOutfitContextMenu::onVisible(LLSD::String param) +{ + LLUUID outfit_cat_id = mUUIDs.back(); + bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id; + + if ("edit" == param) + { + return is_worn; + } + else if ("wear_replace" == param) + { + return !is_worn; + } + else if ("delete" == param) + { + return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id); + } + + return true; +} + +//static +void LLOutfitContextMenu::editOutfit() +{ + LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit")); +} + +void LLOutfitContextMenu::renameOutfit(const LLUUID& outfit_cat_id) +{ + LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); +} + +LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist) + : mOutfitList(olist), + mMenu(NULL) +{ + llassert_always(mOutfitList); + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenuBase::onWear, this)); + registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenuBase::onTakeOff, this)); + registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenuBase::onRename, this)); + registrar.add("Gear.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); + registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenuBase::onCreate, this, _2)); + registrar.add("Gear.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, mOutfitList)); + registrar.add("Gear.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, mOutfitList)); + + registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this)); + + registrar.add("Gear.UploadPhoto", boost::bind(&LLOutfitListGearMenuBase::onUploadFoto, this)); + + enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2)); + enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenuBase::onVisible, this, _2)); + + mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( + "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + llassert(mMenu); +} + +LLOutfitListGearMenuBase::~LLOutfitListGearMenuBase() +{} + +void LLOutfitListGearMenuBase::updateItemsVisibility() +{ + onUpdateItemsVisibility(); +} + +void LLOutfitListGearMenuBase::onUpdateItemsVisibility() +{ + if (!mMenu) return; + + bool have_selection = getSelectedOutfitID().notNull(); + mMenu->setItemVisible("sepatator1", have_selection); + mMenu->setItemVisible("sepatator2", have_selection); + mMenu->arrangeAndClear(); // update menu height +} + +LLToggleableMenu* LLOutfitListGearMenuBase::getMenu() +{ + return mMenu; +} +const LLUUID& LLOutfitListGearMenuBase::getSelectedOutfitID() +{ + return mOutfitList->getSelectedOutfitUUID(); +} + +LLViewerInventoryCategory* LLOutfitListGearMenuBase::getSelectedOutfit() +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.isNull()) + { + return NULL; + } + + LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); + return cat; +} + +void LLOutfitListGearMenuBase::onWear() +{ + LLViewerInventoryCategory* selected_outfit = getSelectedOutfit(); + if (selected_outfit) + { + LLAppearanceMgr::instance().wearInventoryCategory( + selected_outfit, /*copy=*/ FALSE, /*append=*/ FALSE); + } +} + +void LLOutfitListGearMenuBase::onAdd() +{ + const LLUUID& selected_id = getSelectedOutfitID(); + + if (selected_id.notNull()) + { + LLAppearanceMgr::getInstance()->addCategoryToCurrentOutfit(selected_id); + } +} + +void LLOutfitListGearMenuBase::onTakeOff() +{ + // Take off selected outfit. + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.notNull()) + { + LLAppearanceMgr::instance().takeOffOutfit(selected_outfit_id); + } +} + +void LLOutfitListGearMenuBase::onRename() +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.notNull()) + { + LLAppearanceMgr::instance().renameOutfit(selected_outfit_id); + } +} + +void LLOutfitListGearMenuBase::onCreate(const LLSD& data) +{ + LLWearableType::EType type = LLWearableType::typeNameToType(data.asString()); + if (type == LLWearableType::WT_NONE) + { + LL_WARNS() << "Invalid wearable type" << LL_ENDL; + return; + } + + LLAgentWearables::createWearable(type, true); +} + +bool LLOutfitListGearMenuBase::onEnable(LLSD::String param) +{ + // Handle the "Wear - Replace Current Outfit" menu option specially + // because LLOutfitList::isActionEnabled() checks whether it's allowed + // to wear selected outfit OR selected items, while we're only + // interested in the outfit (STORM-183). + if ("wear" == param) + { + return LLAppearanceMgr::instance().getCanReplaceCOF(mOutfitList->getSelectedOutfitUUID()); + } + + return mOutfitList->isActionEnabled(param); +} + +bool LLOutfitListGearMenuBase::onVisible(LLSD::String param) +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + if (selected_outfit_id.isNull()) // no selection or invalid outfit selected + { + return false; + } + + // *TODO This condition leads to menu item behavior inconsistent with + // "Wear" button behavior and should be modified or removed. + bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == selected_outfit_id; + + if ("wear" == param) + { + return !is_worn; + } + + return true; +} + +void LLOutfitListGearMenuBase::onUploadFoto() +{ + +} + +LLOutfitListGearMenu::LLOutfitListGearMenu(LLOutfitListBase* olist) + : LLOutfitListGearMenuBase(olist) +{} + +LLOutfitListGearMenu::~LLOutfitListGearMenu() +{} + +void LLOutfitListGearMenu::onUpdateItemsVisibility() +{ + if (!mMenu) return; + mMenu->setItemVisible("expand", TRUE); + mMenu->setItemVisible("collapse", TRUE); + mMenu->setItemVisible("upload_photo", FALSE); + mMenu->setItemVisible("load_assets", TRUE); + LLOutfitListGearMenuBase::onUpdateItemsVisibility(); +} + // EOF diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index 2e3fb3f488..6db3efa70d 100755 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -32,11 +32,14 @@ // newview #include "llinventorymodel.h" +#include "lllistcontextmenu.h" #include "llpanelappearancetab.h" +#include "lltoggleablemenu.h" +#include "llviewermenu.h" class LLAccordionCtrlTab; class LLInventoryCategoriesObserver; -class LLOutfitListGearMenu; +class LLOutfitListGearMenuBase; class LLWearableItemsList; class LLListContextMenu; @@ -57,6 +60,137 @@ public: /*virtual*/ bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const; }; +class LLOutfitListBase : public LLPanelAppearanceTab +{ +public: + typedef boost::function<void(const LLUUID&)> selection_change_callback_t; + typedef boost::signals2::signal<void(const LLUUID&)> selection_change_signal_t; + + LLOutfitListBase(); + virtual ~LLOutfitListBase(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& info); + + void refreshList(const LLUUID& category_id); + void computeDifference(const LLInventoryModel::cat_array_t& vcats, uuid_vec_t& vadded, uuid_vec_t& vremoved); + // highlights currently worn outfit in list and unhighlights previously worn + void highlightBaseOutfit(); + void ChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id); + + + virtual void getCurrentCategories(uuid_vec_t& vcur) = 0; + virtual void updateAddedCategory(LLUUID cat_id) = 0; + virtual void updateRemovedCategory(LLUUID cat_id) = 0; + virtual void updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name) = 0; + virtual void sortOutfits(); + + void removeSelected(); + void setSelectedOutfitByUUID(const LLUUID& outfit_uuid); + const LLUUID& getSelectedOutfitUUID() const { return mSelectedOutfitUUID; } + boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb); + void outfitRightClickCallBack(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id); + + virtual bool isActionEnabled(const LLSD& userdata); + virtual void performAction(std::string action); + virtual bool hasItemSelected() = 0; + virtual bool canWearSelected() = 0; + + virtual void deselectOutfit(const LLUUID& category_id); + + void signalSelectionOutfitUUID(const LLUUID& category_id); + + void collapseAllFolders(); + virtual void onCollapseAllFolders() = 0; + + void expandAllFolders(); + virtual void onExpandAllFolders() = 0; + + virtual bool getHasExpandableFolders() = 0; + +protected: + virtual LLOutfitListGearMenuBase* createGearMenu() = 0; + virtual void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id) = 0; + virtual void onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) = 0; + virtual void onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id) = 0; + void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response); + virtual void onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id) = 0; + + bool mIsInitialized; + LLInventoryCategoriesObserver* mCategoriesObserver; + LLUUID mSelectedOutfitUUID; + // id of currently highlited outfit + LLUUID mHighlightedOutfitUUID; + selection_change_signal_t mSelectionChangeSignal; + LLListContextMenu* mOutfitMenu; + LLOutfitListGearMenuBase* mGearMenu; +}; + +////////////////////////////////////////////////////////////////////////// + +class LLOutfitContextMenu : public LLListContextMenu +{ +public: + + LLOutfitContextMenu(LLOutfitListBase* outfit_list) + : LLListContextMenu(), + mOutfitList(outfit_list) + {} +protected: + /* virtual */ LLContextMenu* createMenu(); + + bool onEnable(LLSD::String param); + + bool onVisible(LLSD::String param); + + static void editOutfit(); + + static void renameOutfit(const LLUUID& outfit_cat_id); + +private: + LLOutfitListBase* mOutfitList; +}; + +class LLOutfitListGearMenuBase +{ +public: + LLOutfitListGearMenuBase(LLOutfitListBase* olist); + virtual ~LLOutfitListGearMenuBase(); + + void updateItemsVisibility(); + + LLToggleableMenu* getMenu(); + +protected: + virtual void onUpdateItemsVisibility(); + virtual void onUploadFoto(); + const LLUUID& getSelectedOutfitID(); + + LLOutfitListBase* mOutfitList; + LLToggleableMenu* mMenu; +private: + + LLViewerInventoryCategory* getSelectedOutfit(); + + void onWear(); + void onAdd(); + void onTakeOff(); + void onRename(); + void onCreate(const LLSD& data); + bool onEnable(LLSD::String param); + bool onVisible(LLSD::String param); +}; + +class LLOutfitListGearMenu : public LLOutfitListGearMenuBase +{ +public: + LLOutfitListGearMenu(LLOutfitListBase* olist); + virtual ~LLOutfitListGearMenu(); + +protected: + /*virtual*/ void onUpdateItemsVisibility(); +}; + /** * @class LLOutfitsList * @@ -66,11 +200,9 @@ public: * * Starts fetching necessary inventory content on first opening. */ -class LLOutfitsList : public LLPanelAppearanceTab +class LLOutfitsList : public LLOutfitListBase { public: - typedef boost::function<void (const LLUUID&)> selection_change_callback_t; - typedef boost::signals2::signal<void (const LLUUID&)> selection_change_signal_t; LLOutfitsList(); virtual ~LLOutfitsList(); @@ -79,63 +211,66 @@ public: /*virtual*/ void onOpen(const LLSD& info); - void refreshList(const LLUUID& category_id); - - // highlits currently worn outfit tab text and unhighlights previously worn - void highlightBaseOutfit(); - void performAction(std::string action); + //virtual void refreshList(const LLUUID& category_id); - void removeSelected(); + /*virtual*/ void updateAddedCategory(LLUUID cat_id); + /*virtual*/ void updateRemovedCategory(LLUUID cat_id); - void setSelectedOutfitByUUID(const LLUUID& outfit_uuid); + // highlits currently worn outfit tab text and unhighlights previously worn + /*virtual*/ void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id); - /*virtual*/ void setFilterSubString(const std::string& string); + //void performAction(std::string action); - /*virtual*/ bool isActionEnabled(const LLSD& userdata); - const LLUUID& getSelectedOutfitUUID() const { return mSelectedOutfitUUID; } + /*virtual*/ void setFilterSubString(const std::string& string); /*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const; - boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb); - - // Collects selected items from all selected lists and wears them(if possible- adds, else replaces) + // Collects selected items from all selected lists and wears them(if possible- adds, else replaces) void wearSelectedItems(); /** * Returns true if there is a selection inside currently selected outfit */ - bool hasItemSelected(); + /*virtual*/ bool hasItemSelected(); /** Collapses all outfit accordions. */ - void collapse_all_folders(); + /*virtual*/ void onCollapseAllFolders(); /** Expands all outfit accordions. */ - void expand_all_folders(); + void onExpandAllFolders(); + /*virtual*/ bool getHasExpandableFolders() { return TRUE; } -private: +protected: + LLOutfitListGearMenuBase* createGearMenu(); - void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response); +private: /** * Wrapper for LLCommonUtils::computeDifference. @see LLCommonUtils::computeDifference */ - void computeDifference(const LLInventoryModel::cat_array_t& vcats, uuid_vec_t& vadded, uuid_vec_t& vremoved); + //void computeDifference(const LLInventoryModel::cat_array_t& vcats, uuid_vec_t& vadded, uuid_vec_t& vremoved); + + void getCurrentCategories(uuid_vec_t& vcur); /** * Updates tab displaying outfit identified by category_id. */ - void updateOutfitTab(const LLUUID& category_id); + /*virtual*/ void updateChangedCategoryName(LLViewerInventoryCategory *cat, std::string name); + + /*virtual*/ void sortOutfits(); + + /*virtual*/ void onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid); /** * Resets previous selection and stores newly selected list and outfit id. */ - void changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id); + /*virtual*/ void onChangeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id); /** *Resets items selection inside outfit @@ -143,14 +278,9 @@ private: void resetItemSelection(LLWearableItemsList* list, const LLUUID& category_id); /** - * Saves newly selected outfit ID. - */ - void setSelectedOutfitUUID(const LLUUID& category_id); - - /** * Removes the outfit from selection. */ - void deselectOutfit(const LLUUID& category_id); + /*virtual*/ void deselectOutfit(const LLUUID& category_id); /** * Try restoring selection for a temporary hidden tab. @@ -182,15 +312,16 @@ private: */ bool canWearSelected(); - void onAccordionTabRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id); void onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y); void onCOFChanged(); - void onSelectionChange(LLUICtrl* ctrl); + void onListSelectionChange(LLUICtrl* ctrl); + + /*virtual*/ void onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id); static void onOutfitRename(const LLSD& notification, const LLSD& response); - LLInventoryCategoriesObserver* mCategoriesObserver; + //LLInventoryCategoriesObserver* mCategoriesObserver; LLAccordionCtrl* mAccordion; LLPanel* mListCommands; @@ -199,11 +330,6 @@ private: typedef wearables_lists_map_t::value_type wearables_lists_map_value_t; wearables_lists_map_t mSelectedListsMap; - LLUUID mSelectedOutfitUUID; - // id of currently highlited outfit - LLUUID mHighlightedOutfitUUID; - selection_change_signal_t mSelectionChangeSignal; - typedef std::map<LLUUID, LLAccordionCtrlTab*> outfits_map_t; typedef outfits_map_t::value_type outfits_map_value_t; outfits_map_t mOutfitsMap; @@ -212,10 +338,9 @@ private: // Used to monitor COF changes for updating items worn state. See EXT-8636. uuid_vec_t mCOFLinkedItems; - LLOutfitListGearMenu* mGearMenu; - LLListContextMenu* mOutfitMenu; + //LLOutfitListGearMenu* mGearMenu; - bool mIsInitialized; + //bool mIsInitialized; /** * True if there is a selection inside currently selected outfit */ diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 1e1f59055f..eb88abb2bf 100755 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -37,6 +37,7 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "lloutfitobserver.h" +#include "lloutfitgallery.h" #include "lloutfitslist.h" #include "llpanelwearing.h" #include "llsaveoutfitcombobtn.h" @@ -44,6 +45,7 @@ #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 LLPanelInjector<LLPanelOutfitsInventory> t_inventory("panel_outfits_inventory"); @@ -268,12 +270,16 @@ bool LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) void LLPanelOutfitsInventory::initTabPanels() { + //TODO: Add LLOutfitGallery change callback mCurrentOutfitPanel = findChild<LLPanelWearing>(COF_TAB_NAME); mCurrentOutfitPanel->setSelectionChangeCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); mMyOutfitsPanel = findChild<LLOutfitsList>(OUTFITS_TAB_NAME); mMyOutfitsPanel->setSelectionChangeCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); + mOutfitGalleryPanel = findChild<LLOutfitGallery>(OUTFIT_GALLERY_TAB_NAME); + mOutfitGalleryPanel->setSelectionChangeCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this)); + mAppearanceTabs = getChild<LLTabContainer>("appearance_tabs"); mAppearanceTabs->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::onTabChange, this)); } diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index a7917b457c..8c873df038 100755 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -30,8 +30,9 @@ #include "llpanel.h" +class LLOutfitGallery; class LLOutfitsList; -class LLOutfitListGearMenu; +class LLOutfitListGearMenuBase; class LLPanelAppearanceTab; class LLPanelWearing; class LLMenuGL; @@ -76,6 +77,7 @@ protected: private: LLPanelAppearanceTab* mActivePanel; LLOutfitsList* mMyOutfitsPanel; + LLOutfitGallery* mOutfitGalleryPanel; LLPanelWearing* mCurrentOutfitPanel; // tab panels // diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 980810835e..1e9d0f912f 100755 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -71,6 +71,7 @@ #include "llradiogroup.h" #include "llfloaterreg.h" #include "lllocalbitmaps.h" +#include "llerror.h" static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f; static const F32 CONTEXT_CONE_OUT_ALPHA = 1.f; @@ -573,6 +574,7 @@ void LLFloaterTexturePicker::draw() mTexturep = NULL; if(mImageAssetID.notNull()) { + LL_WARNS() << "mImageAssetID: " << mImageAssetID << LL_ENDL; mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID); mTexturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW); } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 8533625e50..e0da7f5d9e 100755 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -948,4 +948,13 @@ <color name="SyntaxLslStringLiteral" value="1 0.14 0 1" /> + <color + name="OutfitGalleryItemSelected" + value="0.22 0.45 0.35 1" /> + <color + name="OutfitGalleryItemWorn" + value="0.33 0.58 0.47 1" /> + <color + name="OutfitGalleryItemUnselected" + value="0.4 0.4 0.4 1" /> </colors> diff --git a/indra/newview/skins/default/textures/icons/Default_Outfit_Photo.png b/indra/newview/skins/default/textures/icons/Default_Outfit_Photo.png Binary files differnew file mode 100644 index 0000000000..bacddcbb68 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Default_Outfit_Photo.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index e453d94883..be23cbfeb7 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -804,6 +804,7 @@ with the same filename but different name <texture name="Camera_Drag_Dot" file_name="world/CameraDragDot.png"/> <texture name="NavBar Separator" file_name="navbar/separator.png"/> + <texture name="Default_Outfit_Photo" file_name="icons/Default_Outfit_Photo.png" preload="true"/> <texture name="Notification_Condense" file_name="icons/Icon_Notification_Condense.png" preload="true"/> <texture name="Notification_Expand" file_name="icons/Icon_Notification_Expand.png" preload="true"/> <texture name="System_Notification" file_name="icons/SL_Logo.png" preload="true"/> diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml index 76adaad57c..420aced6a2 100755 --- a/indra/newview/skins/default/xui/en/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml @@ -406,7 +406,7 @@ width="400" height="400" follows="top|left"/> - <view_border + <!--view_border bevel_style="in" height="21" layout="topleft" @@ -414,7 +414,7 @@ top_pad="0" right="-10" follows="left|top|right" - left_delta="0"/> + left_delta="0"/>--> <text type="string" font="SansSerifSmall" diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml index 3b8ace6308..e31ff2a6e2 100755 --- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml @@ -39,8 +39,15 @@ function="Gear.OnVisible" parameter="take_off" /> </menu_item_call> + <menu_item_call + label="Upload Photo (L$10)" + layout="topleft" + name="upload_photo"> + <on_click + function="Gear.UploadPhoto" /> + </menu_item_call> - <menu_item_separator name="sepatator1" /> + <menu_item_separator name="sepatator1" /> <!-- copied (with minor modifications) from menu_inventory_add.xml --> <!-- *TODO: generate dynamically? --> <menu diff --git a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml new file mode 100644 index 0000000000..c06c6a86c3 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + background_visible="true" + bg_alpha_color="DkGray" + border="false" + follows="all" + height="430" + name="Outfit Gallery" + layout="topleft" + left="0" + top="0" + width="312"> + <string name="outfit_photo_string"> + Photo of "[OUTFIT_NAME]" outfit + </string> + <scroll_container + border="true" + bevel_style="none" + follows="all" + height="400" + width="312" + min_width="312" + layout="topleft" + left="4" + top="0" + name="gallery_scroll_panel" + opaque="false" + top_pad="0"> + <!--outfit_gallery_item + layout="topleft" + left="10" + name="preview_outfit1" + height="175" + width="150" + follows="left|top"/--> + <!--layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="0" name="top_gallery_stack" orientation="horizontal"> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> + </layout_panel> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> + </layout_panel> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> + </layout_panel> + </layout_stack> + <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="190" name="top_gallery_stack" orientation="horizontal"> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> + </layout_panel> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> + </layout_panel> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> + </layout_panel> + </layout_stack> + <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="380" name="top_gallery_stack" orientation="horizontal"> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> + </layout_panel> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> + </layout_panel> + <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> + <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> + </layout_panel> + </layout_stack--> + <!--</panel>--> + </scroll_container> + <panel + background_visible="true" + follows="bottom|left|right" + height="28" + layout="topleft" + left="4" + top_pad="0" + visible="true" + name="bottom_panel" + width="312"> + <menu_button + follows="bottom|left" + tool_tip="Show additional options" + height="25" + image_hover_unselected="Toolbar_Left_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Left_Selected" + image_unselected="Toolbar_Left_Off" + layout="topleft" + left="0" + name="options_gear_btn" + top="1" + width="31" /> + <icon + follows="bottom|left|right" + height="25" + image_name="Toolbar_Middle_Off" + layout="topleft" + left_pad="1" + name="dummy_icon" + width="243"/> + <button + follows="bottom|right" + height="25" + image_hover_unselected="Toolbar_Right_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Right_Selected" + image_unselected="Toolbar_Right_Off" + layout="topleft" + left_pad="1" + name="trash_btn" + tool_tip="Delete selected outfit" + width="31"/> + </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_outfit_gallery_item.xml b/indra/newview/skins/default/xui/en/panel_outfit_gallery_item.xml new file mode 100644 index 0000000000..eede209e91 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_outfit_gallery_item.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + background_visible="false" + background_opaque="false" + bg_alpha_color="FrogGreen" + bg_opaque_color="FrogGreen" + border_color="Red" + border="false" + bevel_style="none" + follows="left|top" + height="169" + width="150" + name="gallery_item_panel" + layout="topleft" + left="0" + top="0" + > + <string name="worn_string"> + (worn) + </string> + <icon + left="1" + top="0" + layout="topleft" + name="preview_outfit" + height="149" + width="147" + follows="left|top" + visible="true" + image_name="Default_Outfit_Photo" + /> + <panel + background_visible="false" + background_opaque="true" + bg_opaque_color="OutfitGalleryItemSelected" + border="false" + bevel_style="none" + follows="left|top" + left="0" + top="149" + height="25" + width="148" + name="text_bg_panel" + > + <text + read_only="true" + length="1" + follows="left|top" + left="1" + height="10" + layout="topleft" + name="outfit_name" + top="0" + width="150" + use_ellipses="true"> + Summer hipster, Pierce Pierce Pierce Pierce + </text> + <text + read_only="true" + length="1" + follows="left|top" + left="1" + height="10" + layout="topleft" + name="outfit_worn_text" + top="10" + width="150"> + (worn) + </text> + </panel> + +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml b/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml index 405d9513db..ff0714adbb 100755 --- a/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml @@ -33,6 +33,17 @@ top="8" width="315"> <panel + class="outfit_gallery" + filename="panel_outfit_gallery.xml" + height="520" + name="outfit_gallery_tab" + background_visible="true" + help_topic="outfit_gallery_tab" + follows="all" + label="OUTFIT GALLERY" + layout="topleft" + width="315" /> + <panel class="outfits_list" filename="panel_outfits_list.xml" height="520" |