summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rwxr-xr-xindra/newview/CMakeLists.txt2
-rwxr-xr-xindra/newview/llinventorybridge.cpp5
-rw-r--r--indra/newview/lloutfitgallery.cpp770
-rw-r--r--indra/newview/lloutfitgallery.h223
-rwxr-xr-xindra/newview/lloutfitslist.cpp1178
-rwxr-xr-xindra/newview/lloutfitslist.h209
-rwxr-xr-xindra/newview/llpaneloutfitsinventory.cpp6
-rwxr-xr-xindra/newview/llpaneloutfitsinventory.h4
-rwxr-xr-xindra/newview/lltexturectrl.cpp2
-rwxr-xr-xindra/newview/skins/default/colors.xml9
-rw-r--r--indra/newview/skins/default/textures/icons/Default_Outfit_Photo.pngbin0 -> 19459 bytes
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml1
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_snapshot.xml4
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_outfit_gear.xml9
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfit_gallery.xml115
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfit_gallery_item.xml72
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_outfits_inventory.xml11
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
new file mode 100644
index 0000000000..bacddcbb68
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Default_Outfit_Photo.png
Binary files differ
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"