summaryrefslogtreecommitdiff
path: root/indra/newview/lloutfitgallery.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 21:25:21 +0200
committerAndrey Lihatskiy <alihatskiy@productengine.com>2024-05-22 22:40:26 +0300
commite2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch)
tree1bb897489ce524986f6196201c10ac0d8861aa5f /indra/newview/lloutfitgallery.cpp
parent069ea06848f766466f1a281144c82a0f2bd79f3a (diff)
Fix line endlings
Diffstat (limited to 'indra/newview/lloutfitgallery.cpp')
-rw-r--r--indra/newview/lloutfitgallery.cpp2646
1 files changed, 1323 insertions, 1323 deletions
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index 0da0ae59a4..5d55816f94 100644
--- a/indra/newview/lloutfitgallery.cpp
+++ b/indra/newview/lloutfitgallery.cpp
@@ -1,1323 +1,1323 @@
-/**
- * @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"
-
-// llcommon
-#include "llcommonutils.h"
-#include "llfilesystem.h"
-
-#include "llaccordionctrltab.h"
-#include "llappearancemgr.h"
-#include "llerror.h"
-#include "llfilepicker.h"
-#include "llfloaterperms.h"
-#include "llfloaterreg.h"
-#include "llfloatersimplesnapshot.h"
-#include "llimagedimensionsinfo.h"
-#include "llinventoryfunctions.h"
-#include "llinventorymodel.h"
-#include "lllocalbitmaps.h"
-#include "llnotificationsutil.h"
-#include "llpaneloutfitsinventory.h"
-#include "lltabcontainer.h"
-#include "lltexturectrl.h"
-#include "lltrans.h"
-#include "llviewercontrol.h"
-#include "llviewermenufile.h"
-#include "llviewertexturelist.h"
-#include "llwearableitemslist.h"
-
-static LLPanelInjector<LLOutfitGallery> t_outfit_gallery("outfit_gallery");
-
-#define MAX_OUTFIT_PHOTO_WIDTH 256
-#define MAX_OUTFIT_PHOTO_HEIGHT 256
-
-const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
-
-LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p)
- : LLOutfitListBase(),
- mOutfitsObserver(NULL),
- mScrollPanel(NULL),
- mGalleryPanel(NULL),
- mLastRowPanel(NULL),
- mGalleryCreated(false),
- mRowCount(0),
- mItemsAddedCount(0),
- mOutfitLinkPending(NULL),
- mOutfitRenamePending(NULL),
- mSnapshotFolderID(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),
- mRowPanWidthFactor(p.row_panel_width_factor),
- mGalleryWidthFactor(p.gallery_width_factor),
- mTextureSelected(NULL)
-{
- updateGalleryWidth();
-}
-
-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", GALLERY_ITEMS_PER_ROW_MIN),
- 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");
- LLPanel::Params params = LLPanel::getDefaultParams(); // Don't parse XML when creating dummy LLPanel
- mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params);
- mMessageTextBox = getChild<LLTextBox>("no_outfits_txt");
- mOutfitGalleryMenu = new LLOutfitGalleryContextMenu(this);
- return rv;
-}
-
-void LLOutfitGallery::onOpen(const LLSD& info)
-{
- LLOutfitListBase::onOpen(info);
- if (!mGalleryCreated)
- {
- 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]]);
- }
- reArrangeRows();
- mGalleryCreated = true;
- }
-}
-
-void LLOutfitGallery::draw()
-{
- LLPanel::draw();
- if (mGalleryCreated)
- {
- updateRowsIfNeeded();
- }
-}
-
-bool LLOutfitGallery::handleKeyHere(KEY key, MASK mask)
-{
- bool handled = false;
- switch (key)
- {
- case KEY_RETURN:
- // Open selected items if enter key hit on the inventory panel
- if (mask == MASK_NONE && mSelectedOutfitUUID.notNull())
- {
- // Or should it wearSelectedOutfit?
- getSelectedItem()->openOutfitsContent();
- }
- handled = true;
- break;
- case KEY_DELETE:
-#if LL_DARWIN
- case KEY_BACKSPACE:
-#endif
- // Delete selected items if delete or backspace key hit on the inventory panel
- // Note: on Mac laptop keyboards, backspace and delete are one and the same
- if (mSelectedOutfitUUID.notNull())
- {
- onRemoveOutfit(mSelectedOutfitUUID);
- }
- handled = true;
- break;
-
- case KEY_F2:
- LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID);
- handled = true;
- break;
-
- case KEY_PAGE_UP:
- if (mScrollPanel)
- {
- mScrollPanel->pageUp(30);
- }
- handled = true;
- break;
-
- case KEY_PAGE_DOWN:
- if (mScrollPanel)
- {
- mScrollPanel->pageDown(30);
- }
- handled = true;
- break;
-
- case KEY_HOME:
- if (mScrollPanel)
- {
- mScrollPanel->goToTop();
- }
- handled = true;
- break;
-
- case KEY_END:
- if (mScrollPanel)
- {
- mScrollPanel->goToBottom();
- }
- handled = true;
- break;
-
- case KEY_LEFT:
- moveLeft();
- handled = true;
- break;
-
- case KEY_RIGHT:
- moveRight();
- handled = true;
- break;
-
- case KEY_UP:
- moveUp();
- handled = true;
- break;
-
- case KEY_DOWN:
- moveDown();
- handled = true;
- break;
-
- default:
- break;
- }
-
- if (handled)
- {
- mOutfitGalleryMenu->hide();
- }
-
- return handled;
-}
-
-void LLOutfitGallery::moveUp()
-{
- if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
- {
- LLOutfitGalleryItem* item = getSelectedItem();
- if (item)
- {
- S32 n = mItemIndexMap[item];
- n -= mItemsInRow;
- if (n >= 0)
- {
- item = mIndexToItemMap[n];
- LLUUID item_id = item->getUUID();
- ChangeOutfitSelection(nullptr, item_id);
- item->setFocus(true);
-
- scrollToShowItem(mSelectedOutfitUUID);
- }
- }
- }
-}
-
-void LLOutfitGallery::moveDown()
-{
- if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
- {
- LLOutfitGalleryItem* item = getSelectedItem();
- if (item)
- {
- S32 n = mItemIndexMap[item];
- n += mItemsInRow;
- if (n < mItemsAddedCount)
- {
- item = mIndexToItemMap[n];
- LLUUID item_id = item->getUUID();
- ChangeOutfitSelection(nullptr, item_id);
- item->setFocus(true);
-
- scrollToShowItem(mSelectedOutfitUUID);
- }
- }
- }
-}
-
-void LLOutfitGallery::moveLeft()
-{
- if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
- {
- LLOutfitGalleryItem* item = getSelectedItem();
- if (item)
- {
- // Might be better to get item from panel
- S32 n = mItemIndexMap[item];
- n--;
- if (n < 0)
- {
- n = mItemsAddedCount - 1;
- }
- item = mIndexToItemMap[n];
- LLUUID item_id = item->getUUID();
- ChangeOutfitSelection(nullptr, item_id);
- item->setFocus(true);
-
- scrollToShowItem(mSelectedOutfitUUID);
- }
- }
-}
-
-void LLOutfitGallery::moveRight()
-{
- if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
- {
- LLOutfitGalleryItem* item = getSelectedItem();
- if (item)
- {
- S32 n = mItemIndexMap[item];
- n++;
- if (n == mItemsAddedCount)
- {
- n = 0;
- }
- item = mIndexToItemMap[n];
- LLUUID item_id = item->getUUID();
- ChangeOutfitSelection(nullptr, item_id);
- item->setFocus(true);
-
- scrollToShowItem(mSelectedOutfitUUID);
- }
- }
-}
-
-void LLOutfitGallery::onFocusLost()
-{
- LLOutfitListBase::onFocusLost();
-
- if (mSelectedOutfitUUID.notNull())
- {
- LLOutfitGalleryItem* item = getSelectedItem();
- if (item)
- {
- item->setSelected(false);
- }
- }
-}
-
-void LLOutfitGallery::onFocusReceived()
-{
- LLOutfitListBase::onFocusReceived();
-
- if (mSelectedOutfitUUID.notNull())
- {
- LLOutfitGalleryItem* item = getSelectedItem();
- if (item)
- {
- item->setSelected(true);
- }
- }
-}
-
-void LLOutfitGallery::onRemoveOutfit(const LLUUID& outfit_cat_id)
-{
- LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(onOutfitsRemovalConfirmation, _1, _2, outfit_cat_id));
-}
-
-void LLOutfitGallery::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0) return; // canceled
-
- if (outfit_cat_id.notNull())
- {
- gInventory.removeCategory(outfit_cat_id);
- }
-}
-
-void LLOutfitGallery::scrollToShowItem(const LLUUID& item_id)
-{
- LLOutfitGalleryItem* item = mOutfitMap[item_id];
- if (item)
- {
- const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect();
-
- LLRect item_rect;
- item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel);
- LLRect overlap_rect(item_rect);
- overlap_rect.intersectWith(visible_content_rect);
-
- //Scroll when the selected item is outside the visible area
- if (overlap_rect.getHeight() + 5 < item->getRect().getHeight())
- {
- LLRect content_rect = mScrollPanel->getContentWindowRect();
- LLRect constraint_rect;
- constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
-
- LLRect item_doc_rect;
- item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel);
-
- mScrollPanel->scrollToShowRect(item_doc_rect, constraint_rect);
- }
- }
-}
-
-void LLOutfitGallery::updateRowsIfNeeded()
-{
- if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1)
- {
- reArrangeRows(1);
- }
- else if((mRowPanelWidth > (getRect().getWidth() + mItemHorizontalGap)) && mItemsInRow > GALLERY_ITEMS_PER_ROW_MIN)
- {
- reArrangeRows(-1);
- }
-}
-
-bool compareGalleryItem(LLOutfitGalleryItem* item1, LLOutfitGalleryItem* item2)
-{
- if(gSavedSettings.getBOOL("OutfitGallerySortByName") ||
- ((item1->isDefaultImage() && item2->isDefaultImage()) || (!item1->isDefaultImage() && !item2->isDefaultImage())))
- {
- std::string name1 = item1->getItemName();
- std::string name2 = item2->getItemName();
-
- return (LLStringUtil::compareDict(name1, name2) < 0);
- }
- else
- {
- return item2->isDefaultImage();
- }
-}
-
-void LLOutfitGallery::reArrangeRows(S32 row_diff)
-{
- std::vector<LLOutfitGalleryItem*> buf_items = mItems;
- for (std::vector<LLOutfitGalleryItem*>::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it)
- {
- removeFromGalleryLast(*it);
- }
- for (std::vector<LLOutfitGalleryItem*>::const_reverse_iterator it = mHiddenItems.rbegin(); it != mHiddenItems.rend(); ++it)
- {
- buf_items.push_back(*it);
- }
- mHiddenItems.clear();
-
- mItemsInRow += row_diff;
- updateGalleryWidth();
- std::sort(buf_items.begin(), buf_items.end(), compareGalleryItem);
-
- std::string cur_filter = getFilterSubString();
- LLStringUtil::toUpper(cur_filter);
-
- for (std::vector<LLOutfitGalleryItem*>::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it)
- {
- std::string outfit_name = (*it)->getItemName();
- LLStringUtil::toUpper(outfit_name);
-
- bool hidden = (std::string::npos == outfit_name.find(cur_filter));
- (*it)->setHidden(hidden);
-
- addToGallery(*it);
- }
-
- updateMessageVisibility();
-}
-
-void LLOutfitGallery::updateGalleryWidth()
-{
- mRowPanelWidth = mRowPanWidthFactor * mItemsInRow - mItemHorizontalGap;
- mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap;
-}
-
-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);
- mUnusedRowPanels.push_back(mLastRowPanel);
- mRowPanels.pop_back();
- if (mRowPanels.size() > 0)
- {
- // Just removed last row
- mLastRowPanel = mRowPanels.back();
- }
- else
- {
- mLastRowPanel = NULL;
- }
-}
-
-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)
-{
- if(item->isHidden())
- {
- mHiddenItems.push_back(item);
- return;
- }
- mItemIndexMap[item] = mItemsAddedCount;
- mIndexToItemMap[mItemsAddedCount] = item;
- mItemsAddedCount++;
- 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)
-{
- if(item->isHidden())
- {
- mHiddenItems.pop_back();
- return;
- }
- 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--;
- mIndexToItemMap.erase(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)
-{
- if(item->isHidden())
- {
- mHiddenItems.erase(std::remove(mHiddenItems.begin(), mHiddenItems.end(), item), mHiddenItems.end());
- return;
- }
- int n = mItemIndexMap[item];
- mItemIndexMap.erase(item);
- mIndexToItemMap.erase(n);
- 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());
- mUnusedItemPanels.push_back(mItemPanels.back());
- mItemPanels.pop_back();
-}
-
-LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID outfit_id)
-{
- LLOutfitGalleryItem::Params giparams;
- LLOutfitGalleryItem* gitem = LLUICtrlFactory::create<LLOutfitGalleryItem>(giparams);
- gitem->reshape(mItemWidth, mItemHeight);
- gitem->setVisible(true);
- gitem->setFollowsLeft();
- gitem->setFollowsTop();
- gitem->setOutfitName(name);
- gitem->setUUID(outfit_id);
- gitem->setGallery(this);
- return gitem;
-}
-
-LLOutfitGalleryItem* LLOutfitGallery::getSelectedItem()
-{
- return mOutfitMap[mSelectedOutfitUUID];
-}
-
-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 = NULL;
- if(mUnusedItemPanels.empty())
- {
- lpanel = LLUICtrlFactory::create<LLPanel>(lpparams);
- }
- else
- {
- lpanel = mUnusedItemPanels.back();
- mUnusedItemPanels.pop_back();
- }
- 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 = NULL;
- if(mUnusedRowPanels.empty())
- {
- stack = LLUICtrlFactory::create<LLPanel>(sparams);
- }
- else
- {
- stack = mUnusedRowPanels.back();
- mUnusedRowPanels.pop_back();
- }
- 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()
-{
- delete mOutfitGalleryMenu;
-
- if (gInventory.containsObserver(mOutfitsObserver))
- {
- gInventory.removeObserver(mOutfitsObserver);
- }
- delete mOutfitsObserver;
-
- while (!mUnusedRowPanels.empty())
- {
- LLPanel* panelp = mUnusedRowPanels.back();
- mUnusedRowPanels.pop_back();
- panelp->die();
- }
- while (!mUnusedItemPanels.empty())
- {
- LLPanel* panelp = mUnusedItemPanels.back();
- mUnusedItemPanels.pop_back();
- panelp->die();
- }
-}
-
-// virtual
-void LLOutfitGallery::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string)
-{
- reArrangeRows();
-}
-
-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, cat_id);
- 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), true);
-
- 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);
- mOutfitGalleryMenu->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);
- }
- // mSelectedOutfitUUID will be set in LLOutfitListBase::ChangeOutfitSelection
-}
-
-void LLOutfitGallery::wearSelectedOutfit()
-{
- LLAppearanceMgr::instance().replaceCurrentOutfit(getSelectedOutfitUUID());
-}
-
-bool LLOutfitGallery::hasItemSelected()
-{
- return false;
-}
-
-bool LLOutfitGallery::canWearSelected()
-{
- return false;
-}
-
-bool LLOutfitGallery::hasDefaultImage(const LLUUID& outfit_cat_id)
-{
- if (mOutfitMap[outfit_cat_id])
- {
- return mOutfitMap[outfit_cat_id]->isDefaultImage();
- }
- return false;
-}
-
-void LLOutfitGallery::updateMessageVisibility()
-{
- if (mItems.empty())
- {
- mMessageTextBox->setVisible(true);
- mScrollPanel->setVisible(false);
- std::string message = getString(getFilterSubString().empty() ? "no_outfits_msg" : "no_matched_outfits_msg");
- mMessageTextBox->setValue(message);
- }
- else
- {
- mScrollPanel->setVisible(true);
- mMessageTextBox->setVisible(false);
- }
-}
-
-LLOutfitListGearMenuBase* LLOutfitGallery::createGearMenu()
-{
- return new LLOutfitGalleryGearMenu(this);
-}
-
-static LLDefaultChildRegistry::Register<LLOutfitGalleryItem> r("outfit_gallery_item");
-
-LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p)
- : LLPanel(p),
- mGallery(nullptr),
- mTexturep(nullptr),
- mSelected(false),
- mWorn(false),
- mDefaultImage(true),
- mOutfitName(""),
- mUUID(LLUUID())
-{
- buildFromFile("panel_outfit_gallery_item.xml");
-}
-
-LLOutfitGalleryItem::~LLOutfitGalleryItem()
-{
-
-}
-
-bool LLOutfitGalleryItem::postBuild()
-{
- setDefaultImage();
-
- mOutfitNameText = getChild<LLTextBox>("outfit_name");
- mOutfitWornText = getChild<LLTextBox>("outfit_worn_text");
- mTextBgPanel = getChild<LLPanel>("text_bg_panel");
- setOutfitWorn(false);
- mHidden = 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)
- {
- if (mImageUpdatePending && mTexturep->getDiscardLevel() >= 0)
- {
- mImageUpdatePending = false;
- if (mTexturep->getOriginalWidth() > MAX_OUTFIT_PHOTO_WIDTH || mTexturep->getOriginalHeight() > MAX_OUTFIT_PHOTO_HEIGHT)
- {
- setDefaultImage();
- }
- }
- else
- {
- 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);
- mOutfitNameText->setToolTip(name);
- mOutfitName = 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("White", LLColor4::white);
- mOutfitWornText->setReadOnlyColor(text_color.get());
- mOutfitNameText->setReadOnlyColor(text_color.get());
- mOutfitWornText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());
- mOutfitNameText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());
- mOutfitWornText->setValue(value ? worn_string : "");
- mOutfitNameText->setText(mOutfitName); // refresh LLTextViewModel to pick up font changes
-}
-
-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);
-}
-
-bool LLOutfitGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- setFocus(true);
- return LLUICtrl::handleRightMouseDown(x, y, mask);
-}
-
-bool LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- return openOutfitsContent() || LLPanel::handleDoubleClick(x, y, mask);
-}
-
-bool LLOutfitGalleryItem::handleKeyHere(KEY key, MASK mask)
-{
- if (!mGallery)
- {
- return false;
- }
-
- bool handled = false;
- switch (key)
- {
- case KEY_LEFT:
- mGallery->moveLeft();
- handled = true;
- break;
-
- case KEY_RIGHT:
- mGallery->moveRight();
- handled = true;
- break;
-
- case KEY_UP:
- mGallery->moveUp();
- handled = true;
- break;
-
- case KEY_DOWN:
- mGallery->moveDown();
- handled = true;
- break;
-
- default:
- break;
- }
- return handled;
-}
-
-void LLOutfitGalleryItem::onFocusLost()
-{
- setSelected(false);
-
- LLPanel::onFocusLost();
-}
-
-void LLOutfitGalleryItem::onFocusReceived()
-{
- setSelected(true);
-
- LLPanel::onFocusReceived();
-}
-
-bool LLOutfitGalleryItem::openOutfitsContent()
-{
- LLTabContainer* appearence_tabs = LLPanelOutfitsInventory::findInstance()->getChild<LLTabContainer>("appearance_tabs");
- if (appearence_tabs && mUUID.notNull())
- {
- appearence_tabs->selectTabByName("outfitslist_tab");
- LLPanel* panel = appearence_tabs->getCurrentPanel();
- if (panel)
- {
- LLAccordionCtrl* accordion = panel->getChild<LLAccordionCtrl>("outfits_accordion");
- LLOutfitsList* outfit_list = dynamic_cast<LLOutfitsList*>(panel);
- if (accordion != NULL && outfit_list != NULL)
- {
- outfit_list->setSelectedOutfitByUUID(mUUID);
- LLAccordionCtrlTab* tab = accordion->getSelectedTab();
- if (tab)
- {
- tab->showAndFocusHeader();
- return true;
- }
- }
- }
- }
- return false;
-}
-
-bool LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id)
-{
- LLPointer<LLViewerFetchedTexture> texture = LLViewerTextureManager::getFetchedTexture(image_asset_id, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- if (texture && texture->getOriginalWidth() <= MAX_OUTFIT_PHOTO_WIDTH && texture->getOriginalHeight() <= MAX_OUTFIT_PHOTO_HEIGHT)
- {
- mImageAssetId = image_asset_id;
- mTexturep = texture;
- getChildView("preview_outfit")->setVisible(false);
- mDefaultImage = false;
- mImageUpdatePending = (texture->getDiscardLevel() == -1);
- return true;
- }
- return false;
-}
-
-LLUUID LLOutfitGalleryItem::getImageAssetId()
-{
- return mImageAssetId;
-}
-
-void LLOutfitGalleryItem::setDefaultImage()
-{
- mTexturep = NULL;
- mImageAssetId.setNull();
- getChildView("preview_outfit")->setVisible(true);
- mDefaultImage = true;
- mImageUpdatePending = false;
-}
-
-LLContextMenu* LLOutfitGalleryContextMenu::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(LLOutfitGallery::onRemoveOutfit, selected_id));
- registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2));
- registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id));
- registrar.add("Outfit.Save", boost::bind(&LLOutfitGalleryContextMenu::onSave, this, selected_id));
- enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2));
- enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2));
-
- return createFromFile("menu_gallery_outfit_tab.xml");
-}
-
-void LLOutfitGalleryContextMenu::onCreate(const LLSD& data)
-{
- LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(data.asString());
- if (type == LLWearableType::WT_NONE)
- {
- LL_WARNS() << "Invalid wearable type" << LL_ENDL;
- return;
- }
-
- LLAgentWearables::createWearable(type, true);
-}
-
-bool LLOutfitGalleryContextMenu::onEnable(LLSD::String param)
-{
- return LLOutfitContextMenu::onEnable(param);
-}
-
-bool LLOutfitGalleryContextMenu::onVisible(LLSD::String param)
-{
- return LLOutfitContextMenu::onVisible(param);
-}
-
-LLOutfitGalleryGearMenu::LLOutfitGalleryGearMenu(LLOutfitListBase* olist)
- : LLOutfitListGearMenuBase(olist)
-{
-}
-
-void LLOutfitGalleryGearMenu::onUpdateItemsVisibility()
-{
- if (!mMenu) return;
- bool have_selection = getSelectedOutfitID().notNull();
- mMenu->setItemVisible("expand", false);
- mMenu->setItemVisible("collapse", false);
- mMenu->setItemVisible("thumbnail", have_selection);
- mMenu->setItemVisible("sepatator3", true);
- mMenu->setItemVisible("sort_folders_by_name", true);
- LLOutfitListGearMenuBase::onUpdateItemsVisibility();
-}
-
-void LLOutfitGalleryGearMenu::onChangeSortOrder()
-{
- bool sort_by_name = !gSavedSettings.getBOOL("OutfitGallerySortByName");
- gSavedSettings.setBOOL("OutfitGallerySortByName", sort_by_name);
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- if (gallery)
- {
- gallery->reArrangeRows();
- }
-}
-
-bool LLOutfitGalleryGearMenu::hasDefaultImage()
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- LLUUID selected_outfit_id = getSelectedOutfitID();
- if (gallery && selected_outfit_id.notNull())
- {
- return gallery->hasDefaultImage(selected_outfit_id);
- }
- return true;
-}
-
-void LLOutfitGallery::onTextureSelectionChanged(LLInventoryItem* itemp)
-{
-}
-
-void LLOutfitGallery::refreshOutfit(const LLUUID& category_id)
-{
- LLViewerInventoryCategory* category = gInventory.getCategory(category_id);
- if (category)
- {
- bool photo_loaded = false;
- LLUUID asset_id = category->getThumbnailUUID();
- if (asset_id.isNull())
- {
- 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);
- for (LLViewerInventoryItem* outfit_item : outfit_item_array)
- {
- LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem();
- LLUUID asset_id, inv_id;
- std::string item_name;
- if (linked_item != NULL)
- {
- if (linked_item->getActualType() == LLAssetType::AT_TEXTURE)
- {
- asset_id = linked_item->getAssetUUID();
- inv_id = linked_item->getUUID();
- item_name = linked_item->getName();
- }
- }
- else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE)
- {
- asset_id = outfit_item->getAssetUUID();
- inv_id = outfit_item->getUUID();
- item_name = outfit_item->getName();
- }
- if (category->getThumbnailUUID().notNull())
- {
- asset_id = category->getThumbnailUUID();
- }
- if (asset_id.notNull())
- {
- photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id);
- // Rename links
- if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name)
- {
- LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending);
- 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(inv_id, updates, NULL);
- mOutfitRenamePending.setNull();
- LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance");
- if (appearance_floater)
- {
- appearance_floater->setFocus(true);
- }
- }
- if (item_name == LLAppearanceMgr::sExpectedTextureName)
- {
- // Images with "appropriate" name take priority
- break;
- }
- }
- if (!photo_loaded)
- {
- mOutfitMap[category_id]->setDefaultImage();
- }
- }
- }
- else
- {
- mOutfitMap[category_id]->setImageAssetId(asset_id);
- }
- }
-
- if (mGalleryCreated && !LLApp::isExiting())
- {
- reArrangeRows();
- }
-}
-
-LLUUID LLOutfitGallery::getPhotoAssetId(const LLUUID& outfit_id)
-{
- outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id);
- if (outfit_it != mOutfitMap.end())
- {
- return outfit_it->second->getImageAssetId();
- }
- return LLUUID();
-}
-
-LLUUID LLOutfitGallery::getDefaultPhoto()
-{
- return LLUUID();
-}
-
+/**
+ * @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"
+
+// llcommon
+#include "llcommonutils.h"
+#include "llfilesystem.h"
+
+#include "llaccordionctrltab.h"
+#include "llappearancemgr.h"
+#include "llerror.h"
+#include "llfilepicker.h"
+#include "llfloaterperms.h"
+#include "llfloaterreg.h"
+#include "llfloatersimplesnapshot.h"
+#include "llimagedimensionsinfo.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "lllocalbitmaps.h"
+#include "llnotificationsutil.h"
+#include "llpaneloutfitsinventory.h"
+#include "lltabcontainer.h"
+#include "lltexturectrl.h"
+#include "lltrans.h"
+#include "llviewercontrol.h"
+#include "llviewermenufile.h"
+#include "llviewertexturelist.h"
+#include "llwearableitemslist.h"
+
+static LLPanelInjector<LLOutfitGallery> t_outfit_gallery("outfit_gallery");
+
+#define MAX_OUTFIT_PHOTO_WIDTH 256
+#define MAX_OUTFIT_PHOTO_HEIGHT 256
+
+const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
+
+LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p)
+ : LLOutfitListBase(),
+ mOutfitsObserver(NULL),
+ mScrollPanel(NULL),
+ mGalleryPanel(NULL),
+ mLastRowPanel(NULL),
+ mGalleryCreated(false),
+ mRowCount(0),
+ mItemsAddedCount(0),
+ mOutfitLinkPending(NULL),
+ mOutfitRenamePending(NULL),
+ mSnapshotFolderID(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),
+ mRowPanWidthFactor(p.row_panel_width_factor),
+ mGalleryWidthFactor(p.gallery_width_factor),
+ mTextureSelected(NULL)
+{
+ updateGalleryWidth();
+}
+
+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", GALLERY_ITEMS_PER_ROW_MIN),
+ 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");
+ LLPanel::Params params = LLPanel::getDefaultParams(); // Don't parse XML when creating dummy LLPanel
+ mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params);
+ mMessageTextBox = getChild<LLTextBox>("no_outfits_txt");
+ mOutfitGalleryMenu = new LLOutfitGalleryContextMenu(this);
+ return rv;
+}
+
+void LLOutfitGallery::onOpen(const LLSD& info)
+{
+ LLOutfitListBase::onOpen(info);
+ if (!mGalleryCreated)
+ {
+ 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]]);
+ }
+ reArrangeRows();
+ mGalleryCreated = true;
+ }
+}
+
+void LLOutfitGallery::draw()
+{
+ LLPanel::draw();
+ if (mGalleryCreated)
+ {
+ updateRowsIfNeeded();
+ }
+}
+
+bool LLOutfitGallery::handleKeyHere(KEY key, MASK mask)
+{
+ bool handled = false;
+ switch (key)
+ {
+ case KEY_RETURN:
+ // Open selected items if enter key hit on the inventory panel
+ if (mask == MASK_NONE && mSelectedOutfitUUID.notNull())
+ {
+ // Or should it wearSelectedOutfit?
+ getSelectedItem()->openOutfitsContent();
+ }
+ handled = true;
+ break;
+ case KEY_DELETE:
+#if LL_DARWIN
+ case KEY_BACKSPACE:
+#endif
+ // Delete selected items if delete or backspace key hit on the inventory panel
+ // Note: on Mac laptop keyboards, backspace and delete are one and the same
+ if (mSelectedOutfitUUID.notNull())
+ {
+ onRemoveOutfit(mSelectedOutfitUUID);
+ }
+ handled = true;
+ break;
+
+ case KEY_F2:
+ LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID);
+ handled = true;
+ break;
+
+ case KEY_PAGE_UP:
+ if (mScrollPanel)
+ {
+ mScrollPanel->pageUp(30);
+ }
+ handled = true;
+ break;
+
+ case KEY_PAGE_DOWN:
+ if (mScrollPanel)
+ {
+ mScrollPanel->pageDown(30);
+ }
+ handled = true;
+ break;
+
+ case KEY_HOME:
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToTop();
+ }
+ handled = true;
+ break;
+
+ case KEY_END:
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToBottom();
+ }
+ handled = true;
+ break;
+
+ case KEY_LEFT:
+ moveLeft();
+ handled = true;
+ break;
+
+ case KEY_RIGHT:
+ moveRight();
+ handled = true;
+ break;
+
+ case KEY_UP:
+ moveUp();
+ handled = true;
+ break;
+
+ case KEY_DOWN:
+ moveDown();
+ handled = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (handled)
+ {
+ mOutfitGalleryMenu->hide();
+ }
+
+ return handled;
+}
+
+void LLOutfitGallery::moveUp()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n -= mItemsInRow;
+ if (n >= 0)
+ {
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(true);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+ }
+}
+
+void LLOutfitGallery::moveDown()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n += mItemsInRow;
+ if (n < mItemsAddedCount)
+ {
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(true);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+ }
+}
+
+void LLOutfitGallery::moveLeft()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ // Might be better to get item from panel
+ S32 n = mItemIndexMap[item];
+ n--;
+ if (n < 0)
+ {
+ n = mItemsAddedCount - 1;
+ }
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(true);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+}
+
+void LLOutfitGallery::moveRight()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n++;
+ if (n == mItemsAddedCount)
+ {
+ n = 0;
+ }
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(true);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+}
+
+void LLOutfitGallery::onFocusLost()
+{
+ LLOutfitListBase::onFocusLost();
+
+ if (mSelectedOutfitUUID.notNull())
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ item->setSelected(false);
+ }
+ }
+}
+
+void LLOutfitGallery::onFocusReceived()
+{
+ LLOutfitListBase::onFocusReceived();
+
+ if (mSelectedOutfitUUID.notNull())
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ item->setSelected(true);
+ }
+ }
+}
+
+void LLOutfitGallery::onRemoveOutfit(const LLUUID& outfit_cat_id)
+{
+ LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(onOutfitsRemovalConfirmation, _1, _2, outfit_cat_id));
+}
+
+void LLOutfitGallery::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option != 0) return; // canceled
+
+ if (outfit_cat_id.notNull())
+ {
+ gInventory.removeCategory(outfit_cat_id);
+ }
+}
+
+void LLOutfitGallery::scrollToShowItem(const LLUUID& item_id)
+{
+ LLOutfitGalleryItem* item = mOutfitMap[item_id];
+ if (item)
+ {
+ const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect();
+
+ LLRect item_rect;
+ item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel);
+ LLRect overlap_rect(item_rect);
+ overlap_rect.intersectWith(visible_content_rect);
+
+ //Scroll when the selected item is outside the visible area
+ if (overlap_rect.getHeight() + 5 < item->getRect().getHeight())
+ {
+ LLRect content_rect = mScrollPanel->getContentWindowRect();
+ LLRect constraint_rect;
+ constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
+
+ LLRect item_doc_rect;
+ item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel);
+
+ mScrollPanel->scrollToShowRect(item_doc_rect, constraint_rect);
+ }
+ }
+}
+
+void LLOutfitGallery::updateRowsIfNeeded()
+{
+ if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1)
+ {
+ reArrangeRows(1);
+ }
+ else if((mRowPanelWidth > (getRect().getWidth() + mItemHorizontalGap)) && mItemsInRow > GALLERY_ITEMS_PER_ROW_MIN)
+ {
+ reArrangeRows(-1);
+ }
+}
+
+bool compareGalleryItem(LLOutfitGalleryItem* item1, LLOutfitGalleryItem* item2)
+{
+ if(gSavedSettings.getBOOL("OutfitGallerySortByName") ||
+ ((item1->isDefaultImage() && item2->isDefaultImage()) || (!item1->isDefaultImage() && !item2->isDefaultImage())))
+ {
+ std::string name1 = item1->getItemName();
+ std::string name2 = item2->getItemName();
+
+ return (LLStringUtil::compareDict(name1, name2) < 0);
+ }
+ else
+ {
+ return item2->isDefaultImage();
+ }
+}
+
+void LLOutfitGallery::reArrangeRows(S32 row_diff)
+{
+ std::vector<LLOutfitGalleryItem*> buf_items = mItems;
+ for (std::vector<LLOutfitGalleryItem*>::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it)
+ {
+ removeFromGalleryLast(*it);
+ }
+ for (std::vector<LLOutfitGalleryItem*>::const_reverse_iterator it = mHiddenItems.rbegin(); it != mHiddenItems.rend(); ++it)
+ {
+ buf_items.push_back(*it);
+ }
+ mHiddenItems.clear();
+
+ mItemsInRow += row_diff;
+ updateGalleryWidth();
+ std::sort(buf_items.begin(), buf_items.end(), compareGalleryItem);
+
+ std::string cur_filter = getFilterSubString();
+ LLStringUtil::toUpper(cur_filter);
+
+ for (std::vector<LLOutfitGalleryItem*>::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it)
+ {
+ std::string outfit_name = (*it)->getItemName();
+ LLStringUtil::toUpper(outfit_name);
+
+ bool hidden = (std::string::npos == outfit_name.find(cur_filter));
+ (*it)->setHidden(hidden);
+
+ addToGallery(*it);
+ }
+
+ updateMessageVisibility();
+}
+
+void LLOutfitGallery::updateGalleryWidth()
+{
+ mRowPanelWidth = mRowPanWidthFactor * mItemsInRow - mItemHorizontalGap;
+ mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap;
+}
+
+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);
+ mUnusedRowPanels.push_back(mLastRowPanel);
+ mRowPanels.pop_back();
+ if (mRowPanels.size() > 0)
+ {
+ // Just removed last row
+ mLastRowPanel = mRowPanels.back();
+ }
+ else
+ {
+ mLastRowPanel = NULL;
+ }
+}
+
+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)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.push_back(item);
+ return;
+ }
+ mItemIndexMap[item] = mItemsAddedCount;
+ mIndexToItemMap[mItemsAddedCount] = item;
+ mItemsAddedCount++;
+ 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)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.pop_back();
+ return;
+ }
+ 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--;
+ mIndexToItemMap.erase(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)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.erase(std::remove(mHiddenItems.begin(), mHiddenItems.end(), item), mHiddenItems.end());
+ return;
+ }
+ int n = mItemIndexMap[item];
+ mItemIndexMap.erase(item);
+ mIndexToItemMap.erase(n);
+ 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());
+ mUnusedItemPanels.push_back(mItemPanels.back());
+ mItemPanels.pop_back();
+}
+
+LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID outfit_id)
+{
+ LLOutfitGalleryItem::Params giparams;
+ LLOutfitGalleryItem* gitem = LLUICtrlFactory::create<LLOutfitGalleryItem>(giparams);
+ gitem->reshape(mItemWidth, mItemHeight);
+ gitem->setVisible(true);
+ gitem->setFollowsLeft();
+ gitem->setFollowsTop();
+ gitem->setOutfitName(name);
+ gitem->setUUID(outfit_id);
+ gitem->setGallery(this);
+ return gitem;
+}
+
+LLOutfitGalleryItem* LLOutfitGallery::getSelectedItem()
+{
+ return mOutfitMap[mSelectedOutfitUUID];
+}
+
+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 = NULL;
+ if(mUnusedItemPanels.empty())
+ {
+ lpanel = LLUICtrlFactory::create<LLPanel>(lpparams);
+ }
+ else
+ {
+ lpanel = mUnusedItemPanels.back();
+ mUnusedItemPanels.pop_back();
+ }
+ 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 = NULL;
+ if(mUnusedRowPanels.empty())
+ {
+ stack = LLUICtrlFactory::create<LLPanel>(sparams);
+ }
+ else
+ {
+ stack = mUnusedRowPanels.back();
+ mUnusedRowPanels.pop_back();
+ }
+ 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()
+{
+ delete mOutfitGalleryMenu;
+
+ if (gInventory.containsObserver(mOutfitsObserver))
+ {
+ gInventory.removeObserver(mOutfitsObserver);
+ }
+ delete mOutfitsObserver;
+
+ while (!mUnusedRowPanels.empty())
+ {
+ LLPanel* panelp = mUnusedRowPanels.back();
+ mUnusedRowPanels.pop_back();
+ panelp->die();
+ }
+ while (!mUnusedItemPanels.empty())
+ {
+ LLPanel* panelp = mUnusedItemPanels.back();
+ mUnusedItemPanels.pop_back();
+ panelp->die();
+ }
+}
+
+// virtual
+void LLOutfitGallery::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string)
+{
+ reArrangeRows();
+}
+
+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, cat_id);
+ 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), true);
+
+ 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);
+ mOutfitGalleryMenu->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);
+ }
+ // mSelectedOutfitUUID will be set in LLOutfitListBase::ChangeOutfitSelection
+}
+
+void LLOutfitGallery::wearSelectedOutfit()
+{
+ LLAppearanceMgr::instance().replaceCurrentOutfit(getSelectedOutfitUUID());
+}
+
+bool LLOutfitGallery::hasItemSelected()
+{
+ return false;
+}
+
+bool LLOutfitGallery::canWearSelected()
+{
+ return false;
+}
+
+bool LLOutfitGallery::hasDefaultImage(const LLUUID& outfit_cat_id)
+{
+ if (mOutfitMap[outfit_cat_id])
+ {
+ return mOutfitMap[outfit_cat_id]->isDefaultImage();
+ }
+ return false;
+}
+
+void LLOutfitGallery::updateMessageVisibility()
+{
+ if (mItems.empty())
+ {
+ mMessageTextBox->setVisible(true);
+ mScrollPanel->setVisible(false);
+ std::string message = getString(getFilterSubString().empty() ? "no_outfits_msg" : "no_matched_outfits_msg");
+ mMessageTextBox->setValue(message);
+ }
+ else
+ {
+ mScrollPanel->setVisible(true);
+ mMessageTextBox->setVisible(false);
+ }
+}
+
+LLOutfitListGearMenuBase* LLOutfitGallery::createGearMenu()
+{
+ return new LLOutfitGalleryGearMenu(this);
+}
+
+static LLDefaultChildRegistry::Register<LLOutfitGalleryItem> r("outfit_gallery_item");
+
+LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p)
+ : LLPanel(p),
+ mGallery(nullptr),
+ mTexturep(nullptr),
+ mSelected(false),
+ mWorn(false),
+ mDefaultImage(true),
+ mOutfitName(""),
+ mUUID(LLUUID())
+{
+ buildFromFile("panel_outfit_gallery_item.xml");
+}
+
+LLOutfitGalleryItem::~LLOutfitGalleryItem()
+{
+
+}
+
+bool LLOutfitGalleryItem::postBuild()
+{
+ setDefaultImage();
+
+ mOutfitNameText = getChild<LLTextBox>("outfit_name");
+ mOutfitWornText = getChild<LLTextBox>("outfit_worn_text");
+ mTextBgPanel = getChild<LLPanel>("text_bg_panel");
+ setOutfitWorn(false);
+ mHidden = 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)
+ {
+ if (mImageUpdatePending && mTexturep->getDiscardLevel() >= 0)
+ {
+ mImageUpdatePending = false;
+ if (mTexturep->getOriginalWidth() > MAX_OUTFIT_PHOTO_WIDTH || mTexturep->getOriginalHeight() > MAX_OUTFIT_PHOTO_HEIGHT)
+ {
+ setDefaultImage();
+ }
+ }
+ else
+ {
+ 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);
+ mOutfitNameText->setToolTip(name);
+ mOutfitName = 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("White", LLColor4::white);
+ mOutfitWornText->setReadOnlyColor(text_color.get());
+ mOutfitNameText->setReadOnlyColor(text_color.get());
+ mOutfitWornText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());
+ mOutfitNameText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());
+ mOutfitWornText->setValue(value ? worn_string : "");
+ mOutfitNameText->setText(mOutfitName); // refresh LLTextViewModel to pick up font changes
+}
+
+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);
+}
+
+bool LLOutfitGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ setFocus(true);
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+}
+
+bool LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return openOutfitsContent() || LLPanel::handleDoubleClick(x, y, mask);
+}
+
+bool LLOutfitGalleryItem::handleKeyHere(KEY key, MASK mask)
+{
+ if (!mGallery)
+ {
+ return false;
+ }
+
+ bool handled = false;
+ switch (key)
+ {
+ case KEY_LEFT:
+ mGallery->moveLeft();
+ handled = true;
+ break;
+
+ case KEY_RIGHT:
+ mGallery->moveRight();
+ handled = true;
+ break;
+
+ case KEY_UP:
+ mGallery->moveUp();
+ handled = true;
+ break;
+
+ case KEY_DOWN:
+ mGallery->moveDown();
+ handled = true;
+ break;
+
+ default:
+ break;
+ }
+ return handled;
+}
+
+void LLOutfitGalleryItem::onFocusLost()
+{
+ setSelected(false);
+
+ LLPanel::onFocusLost();
+}
+
+void LLOutfitGalleryItem::onFocusReceived()
+{
+ setSelected(true);
+
+ LLPanel::onFocusReceived();
+}
+
+bool LLOutfitGalleryItem::openOutfitsContent()
+{
+ LLTabContainer* appearence_tabs = LLPanelOutfitsInventory::findInstance()->getChild<LLTabContainer>("appearance_tabs");
+ if (appearence_tabs && mUUID.notNull())
+ {
+ appearence_tabs->selectTabByName("outfitslist_tab");
+ LLPanel* panel = appearence_tabs->getCurrentPanel();
+ if (panel)
+ {
+ LLAccordionCtrl* accordion = panel->getChild<LLAccordionCtrl>("outfits_accordion");
+ LLOutfitsList* outfit_list = dynamic_cast<LLOutfitsList*>(panel);
+ if (accordion != NULL && outfit_list != NULL)
+ {
+ outfit_list->setSelectedOutfitByUUID(mUUID);
+ LLAccordionCtrlTab* tab = accordion->getSelectedTab();
+ if (tab)
+ {
+ tab->showAndFocusHeader();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id)
+{
+ LLPointer<LLViewerFetchedTexture> texture = LLViewerTextureManager::getFetchedTexture(image_asset_id, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ if (texture && texture->getOriginalWidth() <= MAX_OUTFIT_PHOTO_WIDTH && texture->getOriginalHeight() <= MAX_OUTFIT_PHOTO_HEIGHT)
+ {
+ mImageAssetId = image_asset_id;
+ mTexturep = texture;
+ getChildView("preview_outfit")->setVisible(false);
+ mDefaultImage = false;
+ mImageUpdatePending = (texture->getDiscardLevel() == -1);
+ return true;
+ }
+ return false;
+}
+
+LLUUID LLOutfitGalleryItem::getImageAssetId()
+{
+ return mImageAssetId;
+}
+
+void LLOutfitGalleryItem::setDefaultImage()
+{
+ mTexturep = NULL;
+ mImageAssetId.setNull();
+ getChildView("preview_outfit")->setVisible(true);
+ mDefaultImage = true;
+ mImageUpdatePending = false;
+}
+
+LLContextMenu* LLOutfitGalleryContextMenu::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(LLOutfitGallery::onRemoveOutfit, selected_id));
+ registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2));
+ registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id));
+ registrar.add("Outfit.Save", boost::bind(&LLOutfitGalleryContextMenu::onSave, this, selected_id));
+ enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2));
+ enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2));
+
+ return createFromFile("menu_gallery_outfit_tab.xml");
+}
+
+void LLOutfitGalleryContextMenu::onCreate(const LLSD& data)
+{
+ LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(data.asString());
+ if (type == LLWearableType::WT_NONE)
+ {
+ LL_WARNS() << "Invalid wearable type" << LL_ENDL;
+ return;
+ }
+
+ LLAgentWearables::createWearable(type, true);
+}
+
+bool LLOutfitGalleryContextMenu::onEnable(LLSD::String param)
+{
+ return LLOutfitContextMenu::onEnable(param);
+}
+
+bool LLOutfitGalleryContextMenu::onVisible(LLSD::String param)
+{
+ return LLOutfitContextMenu::onVisible(param);
+}
+
+LLOutfitGalleryGearMenu::LLOutfitGalleryGearMenu(LLOutfitListBase* olist)
+ : LLOutfitListGearMenuBase(olist)
+{
+}
+
+void LLOutfitGalleryGearMenu::onUpdateItemsVisibility()
+{
+ if (!mMenu) return;
+ bool have_selection = getSelectedOutfitID().notNull();
+ mMenu->setItemVisible("expand", false);
+ mMenu->setItemVisible("collapse", false);
+ mMenu->setItemVisible("thumbnail", have_selection);
+ mMenu->setItemVisible("sepatator3", true);
+ mMenu->setItemVisible("sort_folders_by_name", true);
+ LLOutfitListGearMenuBase::onUpdateItemsVisibility();
+}
+
+void LLOutfitGalleryGearMenu::onChangeSortOrder()
+{
+ bool sort_by_name = !gSavedSettings.getBOOL("OutfitGallerySortByName");
+ gSavedSettings.setBOOL("OutfitGallerySortByName", sort_by_name);
+ LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
+ if (gallery)
+ {
+ gallery->reArrangeRows();
+ }
+}
+
+bool LLOutfitGalleryGearMenu::hasDefaultImage()
+{
+ LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
+ LLUUID selected_outfit_id = getSelectedOutfitID();
+ if (gallery && selected_outfit_id.notNull())
+ {
+ return gallery->hasDefaultImage(selected_outfit_id);
+ }
+ return true;
+}
+
+void LLOutfitGallery::onTextureSelectionChanged(LLInventoryItem* itemp)
+{
+}
+
+void LLOutfitGallery::refreshOutfit(const LLUUID& category_id)
+{
+ LLViewerInventoryCategory* category = gInventory.getCategory(category_id);
+ if (category)
+ {
+ bool photo_loaded = false;
+ LLUUID asset_id = category->getThumbnailUUID();
+ if (asset_id.isNull())
+ {
+ 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);
+ for (LLViewerInventoryItem* outfit_item : outfit_item_array)
+ {
+ LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem();
+ LLUUID asset_id, inv_id;
+ std::string item_name;
+ if (linked_item != NULL)
+ {
+ if (linked_item->getActualType() == LLAssetType::AT_TEXTURE)
+ {
+ asset_id = linked_item->getAssetUUID();
+ inv_id = linked_item->getUUID();
+ item_name = linked_item->getName();
+ }
+ }
+ else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE)
+ {
+ asset_id = outfit_item->getAssetUUID();
+ inv_id = outfit_item->getUUID();
+ item_name = outfit_item->getName();
+ }
+ if (category->getThumbnailUUID().notNull())
+ {
+ asset_id = category->getThumbnailUUID();
+ }
+ if (asset_id.notNull())
+ {
+ photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id);
+ // Rename links
+ if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name)
+ {
+ LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending);
+ 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(inv_id, updates, NULL);
+ mOutfitRenamePending.setNull();
+ LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance");
+ if (appearance_floater)
+ {
+ appearance_floater->setFocus(true);
+ }
+ }
+ if (item_name == LLAppearanceMgr::sExpectedTextureName)
+ {
+ // Images with "appropriate" name take priority
+ break;
+ }
+ }
+ if (!photo_loaded)
+ {
+ mOutfitMap[category_id]->setDefaultImage();
+ }
+ }
+ }
+ else
+ {
+ mOutfitMap[category_id]->setImageAssetId(asset_id);
+ }
+ }
+
+ if (mGalleryCreated && !LLApp::isExiting())
+ {
+ reArrangeRows();
+ }
+}
+
+LLUUID LLOutfitGallery::getPhotoAssetId(const LLUUID& outfit_id)
+{
+ outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id);
+ if (outfit_it != mOutfitMap.end())
+ {
+ return outfit_it->second->getImageAssetId();
+ }
+ return LLUUID();
+}
+
+LLUUID LLOutfitGallery::getDefaultPhoto()
+{
+ return LLUUID();
+}
+