summaryrefslogtreecommitdiff
path: root/indra/newview/llinventorygallery.cpp
diff options
context:
space:
mode:
authorMaxim Nikolenko <maximnproductengine@lindenlab.com>2023-03-21 17:47:26 +0200
committerMaxim Nikolenko <maximnproductengine@lindenlab.com>2023-03-21 17:47:26 +0200
commit1eab3247111e66a3b81153173d1624a5c1c9fb72 (patch)
tree3d5cfd8a5ee3bca5194fad8a9f27d519884e844e /indra/newview/llinventorygallery.cpp
parentb68a67491026a055f0de9df349508b9e60a200ed (diff)
SL-19379 WIP Gallery view Inventory: first pass
Diffstat (limited to 'indra/newview/llinventorygallery.cpp')
-rw-r--r--indra/newview/llinventorygallery.cpp924
1 files changed, 924 insertions, 0 deletions
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
new file mode 100644
index 0000000000..b5fd3aaece
--- /dev/null
+++ b/indra/newview/llinventorygallery.cpp
@@ -0,0 +1,924 @@
+/**
+ * @file llinventorygallery.cpp
+ * @brief LLInventoryGallery class implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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"
+
+#include "llinventorygallery.h"
+
+#include "llaccordionctrltab.h"
+#include "llcommonutils.h"
+#include "lliconctrl.h"
+#include "llinventorybridge.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "llthumbnailctrl.h"
+#include "lltextbox.h"
+#include "llviewerfoldertype.h"
+
+#include "llinventoryicon.h"
+
+static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_gallery");
+
+const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
+
+LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p)
+ : LLPanel(),
+ mScrollPanel(NULL),
+ mGalleryPanel(NULL),
+ mLastRowPanel(NULL),
+ mGalleryCreated(false),
+ mRowCount(0),
+ mItemsAddedCount(0),
+ 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),
+ mIsInitialized(false)
+{
+ updateGalleryWidth();
+}
+
+LLInventoryGallery::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 LLInventoryGallery::Params& LLInventoryGallery::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLInventoryGallery>();
+}
+
+BOOL LLInventoryGallery::postBuild()
+{
+ mScrollPanel = getChild<LLScrollContainer>("gallery_scroll_panel");
+ LLPanel::Params params = LLPanel::getDefaultParams();
+ mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params);
+ mMessageTextBox = getChild<LLTextBox>("empty_txt");
+
+ return TRUE;
+}
+
+LLInventoryGallery::~LLInventoryGallery()
+{
+ while (!mUnusedRowPanels.empty())
+ {
+ LLPanel* panelp = mUnusedRowPanels.back();
+ mUnusedRowPanels.pop_back();
+ panelp->die();
+ }
+ while (!mUnusedItemPanels.empty())
+ {
+ LLPanel* panelp = mUnusedItemPanels.back();
+ mUnusedItemPanels.pop_back();
+ panelp->die();
+ }
+
+ if (gInventory.containsObserver(mCategoriesObserver))
+ {
+ gInventory.removeObserver(mCategoriesObserver);
+ }
+ delete mCategoriesObserver;
+}
+
+void LLInventoryGallery::setRootFolder(const LLUUID cat_id)
+{
+ LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
+ if(!category || (mFolderID == cat_id))
+ {
+ return;
+ }
+ if(mFolderID.notNull())
+ {
+ mBackwardFolders.push_back(mFolderID);
+ }
+ mFolderID = cat_id;
+ updateRootFolder();
+}
+
+void LLInventoryGallery::updateRootFolder()
+{
+ if (mIsInitialized)
+ {
+ S32 count = mItemsAddedCount;
+ for (S32 i = count - 1; i >= 0; i--)
+ {
+ updateRemovedItem(mItems[i]->getUUID());
+ }
+
+ if (gInventory.containsObserver(mCategoriesObserver))
+ {
+ gInventory.removeObserver(mCategoriesObserver);
+ }
+ delete mCategoriesObserver;
+ }
+ {
+ mRootChangedSignal();
+
+ mCategoriesObserver = new LLInventoryCategoriesObserver();
+ gInventory.addObserver(mCategoriesObserver);
+
+ // Start observing changes in selected category.
+ mCategoriesObserver->addCategory(mFolderID,
+ boost::bind(&LLInventoryGallery::refreshList, this, mFolderID));
+
+ LLViewerInventoryCategory* category = gInventory.getCategory(mFolderID);
+ //If not all items are fetched now
+ // the observer will refresh the list as soon as the new items
+ // arrive.
+ category->fetch();
+
+ //refreshList(cat_id);
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+
+ gInventory.getDirectDescendentsOf(mFolderID, cat_array, item_array);
+
+ // Creating a vector of newly collected sub-categories UUIDs.
+ for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array->begin();
+ iter != cat_array->end();
+ iter++)
+ {
+ updateAddedItem((*iter)->getUUID());
+ }
+
+ for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin();
+ iter != item_array->end();
+ iter++)
+ {
+ updateAddedItem((*iter)->getUUID());
+ }
+ reArrangeRows();
+ mIsInitialized = true;
+
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToTop();
+ }
+ }
+
+ if (!mGalleryCreated)
+ {
+ initGallery();
+ }
+}
+
+void LLInventoryGallery::initGallery()
+{
+ 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(mItemMap[cats[i]]);
+ }
+ reArrangeRows();
+ mGalleryCreated = true;
+ }
+}
+
+void LLInventoryGallery::draw()
+{
+ LLPanel::draw();
+ if (mGalleryCreated)
+ {
+ updateRowsIfNeeded();
+ }
+}
+
+void LLInventoryGallery::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(LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2)
+{
+ if (item1->getSortGroup() != item2->getSortGroup())
+ {
+ return (item1->getSortGroup() < item2->getSortGroup());
+ }
+ if(((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 LLInventoryGallery::reArrangeRows(S32 row_diff)
+{
+ std::vector<LLInventoryGalleryItem*> buf_items = mItems;
+ for (std::vector<LLInventoryGalleryItem*>::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it)
+ {
+ removeFromGalleryLast(*it);
+ }
+ for (std::vector<LLInventoryGalleryItem*>::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);
+
+ for (std::vector<LLInventoryGalleryItem*>::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it)
+ {
+ (*it)->setHidden(false);
+ applyFilter(*it, mFilterSubString);
+ addToGallery(*it);
+ }
+ updateMessageVisibility();
+}
+
+void LLInventoryGallery::updateGalleryWidth()
+{
+ mRowPanelWidth = mRowPanWidthFactor * mItemsInRow - mItemHorizontalGap;
+ mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap;
+}
+
+LLPanel* LLInventoryGallery::addLastRow()
+{
+ mRowCount++;
+ int row = 0;
+ int vgap = mVerticalGap * row;
+ LLPanel* result = buildRowPanel(0, row * mRowPanelHeight + vgap);
+ mGalleryPanel->addChild(result);
+ return result;
+}
+
+void LLInventoryGallery::moveRowUp(int row)
+{
+ moveRow(row, mRowCount - 1 - row + 1);
+}
+
+void LLInventoryGallery::moveRowDown(int row)
+{
+ moveRow(row, mRowCount - 1 - row - 1);
+}
+
+void LLInventoryGallery::moveRow(int row, int pos)
+{
+ int vgap = mVerticalGap * pos;
+ moveRowPanel(mRowPanels[row], 0, pos * mRowPanelHeight + vgap);
+}
+
+void LLInventoryGallery::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* LLInventoryGallery::addToRow(LLPanel* row_stack, LLInventoryGalleryItem* 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 LLInventoryGallery::addToGallery(LLInventoryGalleryItem* item)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.push_back(item);
+ return;
+ }
+ 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 LLInventoryGallery::removeFromGalleryLast(LLInventoryGalleryItem* 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--;
+
+ 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 LLInventoryGallery::removeFromGalleryMiddle(LLInventoryGalleryItem* item)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.erase(std::remove(mHiddenItems.begin(), mHiddenItems.end(), item), mHiddenItems.end());
+ return;
+ }
+ int n = mItemIndexMap[item];
+ mItemIndexMap.erase(item);
+ std::vector<LLInventoryGalleryItem*> 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 LLInventoryGallery::removeFromLastRow(LLInventoryGalleryItem* item)
+{
+ mItemPanels.back()->removeChild(item);
+ mLastRowPanel->removeChild(mItemPanels.back());
+ mUnusedItemPanels.push_back(mItemPanels.back());
+ mItemPanels.pop_back();
+}
+
+LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id)
+{
+ LLInventoryGalleryItem::Params giparams;
+ LLInventoryGalleryItem* gitem = LLUICtrlFactory::create<LLInventoryGalleryItem>(giparams);
+ gitem->reshape(mItemWidth, mItemHeight);
+ gitem->setVisible(true);
+ gitem->setFollowsLeft();
+ gitem->setFollowsTop();
+ gitem->setName(name);
+ gitem->setUUID(item_id);
+ gitem->setGallery(this);
+ gitem->setType(type);
+ gitem->setThumbnail(thumbnail_id);
+ return gitem;
+}
+
+void LLInventoryGallery::buildGalleryPanel(int row_count)
+{
+ LLPanel::Params params;
+ mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params);
+ reshapeGalleryPanel(row_count);
+}
+
+void LLInventoryGallery::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* LLInventoryGallery::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* LLInventoryGallery::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 LLInventoryGallery::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();
+}
+
+void LLInventoryGallery::setFilterSubString(const std::string& string)
+{
+ mFilterSubString = string;
+ reArrangeRows();
+}
+
+void LLInventoryGallery::applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring)
+{
+ if (!item) return;
+
+ std::string item_name = item->getItemName();
+ LLStringUtil::toUpper(item_name);
+
+ std::string cur_filter = filter_substring;
+ LLStringUtil::toUpper(cur_filter);
+
+ bool hidden = (std::string::npos == item_name.find(cur_filter));
+ item->setHidden(hidden);
+}
+
+void LLInventoryGallery::getCurrentCategories(uuid_vec_t& vcur)
+{
+ for (gallery_item_map_t::const_iterator iter = mItemMap.begin();
+ iter != mItemMap.end();
+ iter++)
+ {
+ if ((*iter).second != NULL)
+ {
+ vcur.push_back((*iter).first);
+ }
+ }
+}
+
+void LLInventoryGallery::updateAddedItem(LLUUID item_id)
+{
+ LLInventoryObject* obj = gInventory.getObject(item_id);
+ if (!obj)
+ {
+ LL_WARNS("InventoryGallery") << "Failed to find item: " << item_id << LL_ENDL;
+ return;
+ }
+
+ LLUUID thumbnail_id = obj->getThumbnailUUID();;
+
+ if ((LLAssetType::AT_CATEGORY == obj->getType()) && thumbnail_id.isNull())
+ {
+ thumbnail_id = getOutfitImageID(item_id);
+ }
+
+ LLInventoryGalleryItem* item = buildGalleryItem(obj->getName(), item_id, obj->getType(), thumbnail_id);
+ mItemMap.insert(LLInventoryGallery::gallery_item_map_t::value_type(item_id, item));
+
+ item->setFocusReceivedCallback(boost::bind(&LLInventoryGallery::onChangeItemSelection, this, item_id));
+ if (mGalleryCreated)
+ {
+ addToGallery(item);
+ }
+}
+
+void LLInventoryGallery::updateRemovedItem(LLUUID item_id)
+{
+ gallery_item_map_t::iterator item_iter = mItemMap.find(item_id);
+ if (item_iter != mItemMap.end())
+ {
+ LLInventoryGalleryItem* item = item_iter->second;
+
+ deselectItem(item_id);
+ mItemMap.erase(item_iter);
+ removeFromGalleryMiddle(item);
+
+ // kill removed item
+ if (item != NULL)
+ {
+ item->die();
+ }
+ }
+
+}
+
+void LLInventoryGallery::updateChangedItemName(LLUUID item_id, std::string name)
+{
+ gallery_item_map_t::iterator iter = mItemMap.find(item_id);
+ if (iter != mItemMap.end())
+ {
+ LLInventoryGalleryItem* item = iter->second;
+ if (item)
+ {
+ item->setName(name);
+ }
+ }
+}
+
+void LLInventoryGallery::onChangeItemSelection(const LLUUID& category_id)
+{
+ if (mSelectedItemID == category_id)
+ return;
+
+ if (mItemMap[mSelectedItemID])
+ {
+ mItemMap[mSelectedItemID]->setSelected(FALSE);
+ }
+ if (mItemMap[category_id])
+ {
+ mItemMap[category_id]->setSelected(TRUE);
+ }
+ mSelectedItemID = category_id;
+ signalSelectionItemID(category_id);
+}
+
+void LLInventoryGallery::updateMessageVisibility()
+{
+ mMessageTextBox->setVisible(mItems.empty());
+ mScrollPanel->setVisible(!mItems.empty());
+}
+
+void LLInventoryGallery::refreshList(const LLUUID& category_id)
+{
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+
+ gInventory.getDirectDescendentsOf(category_id, cat_array, item_array);
+ uuid_vec_t vadded;
+ uuid_vec_t vremoved;
+
+ // Create added and removed items vectors.
+ computeDifference(*cat_array, *item_array, vadded, vremoved);
+
+ // Handle added tabs.
+ for (uuid_vec_t::const_iterator iter = vadded.begin();
+ iter != vadded.end();
+ ++iter)
+ {
+ const LLUUID cat_id = (*iter);
+ updateAddedItem(cat_id);
+ }
+
+ // Handle removed tabs.
+ for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter)
+ {
+ const LLUUID cat_id = (*iter);
+ updateRemovedItem(cat_id);
+ }
+
+ 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)
+ {
+ LLInventoryObject* obj = gInventory.getObject(*items_iter);
+ if(!obj)
+ {
+ return;
+ }
+
+ updateChangedItemName(*items_iter, obj->getName());
+ }
+
+}
+
+void LLInventoryGallery::computeDifference(
+ const LLInventoryModel::cat_array_t vcats,
+ const LLInventoryModel::item_array_t vitems,
+ uuid_vec_t& vadded,
+ uuid_vec_t& vremoved)
+{
+ uuid_vec_t vnew;
+ // Creating a vector of newly collected UUIDs.
+ for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin();
+ iter != vcats.end();
+ iter++)
+ {
+ vnew.push_back((*iter)->getUUID());
+ }
+ for (LLInventoryModel::item_array_t::const_iterator iter = vitems.begin();
+ iter != vitems.end();
+ iter++)
+ {
+ vnew.push_back((*iter)->getUUID());
+ }
+
+ uuid_vec_t vcur;
+ getCurrentCategories(vcur);
+
+ LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved);
+}
+
+void LLInventoryGallery::deselectItem(const LLUUID& category_id)
+{
+ // Reset selection if the item is selected.
+ if (category_id == mSelectedItemID)
+ {
+ mSelectedItemID = LLUUID::null;
+ signalSelectionItemID(mSelectedItemID);
+ }
+}
+
+void LLInventoryGallery::signalSelectionItemID(const LLUUID& category_id)
+{
+ mSelectionChangeSignal(category_id);
+}
+
+boost::signals2::connection LLInventoryGallery::setSelectionChangeCallback(selection_change_callback_t cb)
+{
+ return mSelectionChangeSignal.connect(cb);
+}
+
+LLUUID LLInventoryGallery::getOutfitImageID(LLUUID outfit_id)
+{
+ LLUUID thumbnail_id;
+ LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id);
+ if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ // Not LLIsOfAssetType, because we allow links
+ LLIsTextureType f;
+ gInventory.getDirectDescendentsOf(outfit_id, cats, items, f);
+
+ // Exactly one texture found => show the texture as thumbnail
+ if (1 == items.size())
+ {
+ LLViewerInventoryItem* item = items.front();
+ if (item && item->getIsLinkType())
+ {
+ item = item->getLinkedItem();
+ }
+ if (item)
+ {
+ thumbnail_id = item->getAssetUUID();
+ }
+ }
+ }
+ return thumbnail_id;
+}
+
+boost::signals2::connection LLInventoryGallery::setRootChangedCallback(root_changed_callback_t cb)
+{
+ return mRootChangedSignal.connect(cb);
+}
+
+void LLInventoryGallery::onForwardFolder()
+{
+ if(isForwardAvailable())
+ {
+ mBackwardFolders.push_back(mFolderID);
+ mFolderID = mForwardFolders.back();
+ mForwardFolders.pop_back();
+ updateRootFolder();
+ }
+}
+
+void LLInventoryGallery::onBackwardFolder()
+{
+ if(isBackwardAvailable())
+ {
+ mForwardFolders.push_back(mFolderID);
+ mFolderID = mBackwardFolders.back();
+ mBackwardFolders.pop_back();
+ updateRootFolder();
+ }
+}
+
+void LLInventoryGallery::clearNavigationHistory()
+{
+ mForwardFolders.clear();
+ mBackwardFolders.clear();
+}
+
+bool LLInventoryGallery::isBackwardAvailable()
+{
+ return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back()));
+}
+
+bool LLInventoryGallery::isForwardAvailable()
+{
+ return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back()));
+}
+//-----------------------------
+// LLInventoryGalleryItem
+//-----------------------------
+
+static LLDefaultChildRegistry::Register<LLInventoryGalleryItem> r("inventory_gallery_item");
+
+LLInventoryGalleryItem::LLInventoryGalleryItem(const Params& p)
+ : LLPanel(p),
+ mSelected(false),
+ mDefaultImage(true),
+ mName(""),
+ mUUID(LLUUID()),
+ mIsFolder(true),
+ mGallery(NULL),
+ mType(LLAssetType::AT_NONE),
+ mSortGroup(SG_ITEM)
+{
+ buildFromFile("panel_inventory_gallery_item.xml");
+}
+
+LLInventoryGalleryItem::~LLInventoryGalleryItem()
+{
+}
+
+BOOL LLInventoryGalleryItem::postBuild()
+{
+ mNameText = getChild<LLTextBox>("item_name");
+
+ mTextBgPanel = getChild<LLPanel>("text_bg_panel");
+ mHidden = false;
+ return TRUE;
+}
+
+void LLInventoryGalleryItem::setType(LLAssetType::EType type)
+{
+ mType = type;
+ mIsFolder = (mType == LLAssetType::AT_CATEGORY);
+
+ std::string icon_name = LLInventoryIcon::getIconName(mType);
+ if(mIsFolder)
+ {
+ mSortGroup = SG_NORMAL_FOLDER;
+ LLViewerInventoryCategory * cat = gInventory.getCategory(mUUID);
+ if (cat)
+ {
+ LLFolderType::EType preferred_type = cat->getPreferredType();
+ icon_name = LLViewerFolderType::lookupIconName(preferred_type);
+
+ if (preferred_type == LLFolderType::FT_TRASH)
+ {
+ mSortGroup = SG_TRASH_FOLDER;
+ }
+ else if(LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ mSortGroup = SG_SYSTEM_FOLDER;
+ }
+ }
+ }
+
+ getChild<LLIconCtrl>("item_type")->setValue(icon_name);
+}
+
+void LLInventoryGalleryItem::setThumbnail(LLUUID id)
+{
+ mDefaultImage = id.isNull();
+ if(mDefaultImage)
+ {
+ getChild<LLThumbnailCtrl>("preview_thumbnail")->setValue("Thumbnail_Fallback");
+ }
+ else
+ {
+ getChild<LLThumbnailCtrl>("preview_thumbnail")->setValue(id);
+ }
+}
+
+void LLInventoryGalleryItem::draw()
+{
+ LLPanel::draw();
+
+ // Draw border
+ LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white);
+ LLRect border = getChildView("preview_thumbnail")->getRect();
+ border.mRight = border.mRight + 1;
+ gl_rect_2d(border, border_color.get(), FALSE);
+}
+
+void LLInventoryGalleryItem::setName(std::string name)
+{
+ mNameText->setText(name);
+ mNameText->setToolTip(name);
+ mName = name;
+}
+
+void LLInventoryGalleryItem::setSelected(bool value)
+{
+ mSelected = value;
+ mTextBgPanel->setBackgroundVisible(value);
+}
+
+BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ setFocus(TRUE);
+ return LLUICtrl::handleMouseDown(x, y, mask);
+}
+
+BOOL LLInventoryGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ setFocus(TRUE);
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+}
+
+BOOL LLInventoryGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (mIsFolder && mGallery)
+ {
+ mGallery->setRootFolder(mUUID);
+ }
+ else
+ {
+ LLInvFVBridgeAction::doAction(mUUID,&gInventory);
+ //todo: some item types require different handling
+ }
+
+ return TRUE;
+}
+