summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xindra/llcommon/llfoldertype.cpp4
-rw-r--r--indra/llcommon/llfoldertype.h4
-rwxr-xr-xindra/newview/CMakeLists.txt2
-rwxr-xr-xindra/newview/app_settings/commands.xml10
-rwxr-xr-xindra/newview/app_settings/toolbars.xml1
-rwxr-xr-xindra/newview/llfloatermarketplacelistings.cpp671
-rwxr-xr-xindra/newview/llfloatermarketplacelistings.h190
-rwxr-xr-xindra/newview/llfloateroutbox.cpp2
-rwxr-xr-xindra/newview/llfloateroutbox.h4
-rwxr-xr-xindra/newview/llfolderviewmodelinventory.cpp32
-rwxr-xr-xindra/newview/llfolderviewmodelinventory.h2
-rwxr-xr-xindra/newview/llinventorybridge.cpp507
-rwxr-xr-xindra/newview/llinventorybridge.h8
-rwxr-xr-xindra/newview/llinventoryfilter.cpp48
-rwxr-xr-xindra/newview/llinventoryfilter.h11
-rwxr-xr-xindra/newview/llinventoryfunctions.cpp573
-rwxr-xr-xindra/newview/llinventoryfunctions.h14
-rwxr-xr-xindra/newview/llinventorymodel.cpp16
-rwxr-xr-xindra/newview/llinventorypanel.cpp7
-rwxr-xr-xindra/newview/llmarketplacefunctions.cpp251
-rwxr-xr-xindra/newview/llmarketplacefunctions.h68
-rwxr-xr-xindra/newview/llviewerfloaterreg.cpp5
-rw-r--r--indra/newview/llviewerfoldertype.cpp6
-rwxr-xr-xindra/newview/llviewerinventory.cpp23
-rwxr-xr-xindra/newview/llviewerinventory.h3
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--indra/newview/skins/default/textures/icons/Inv_StockFolderClosed.pngbin0 -> 1143 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/Inv_StockFolderOpen.pngbin0 -> 1319 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/Inv_VersionFolderClosed.pngbin0 -> 1132 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/Inv_VersionFolderOpen.pngbin0 -> 1521 bytes
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml4
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_associate_listing.xml53
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_item_properties.xml23
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_marketplace_listings.xml114
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_marketplace_validation.xml59
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_inventory.xml67
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_marketplace_view.xml16
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml7
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_marketplace_listings.xml161
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml14
40 files changed, 2901 insertions, 80 deletions
diff --git a/indra/llcommon/llfoldertype.cpp b/indra/llcommon/llfoldertype.cpp
index 9c38349cf7..965e4d9734 100755
--- a/indra/llcommon/llfoldertype.cpp
+++ b/indra/llcommon/llfoldertype.cpp
@@ -96,6 +96,10 @@ LLFolderDictionary::LLFolderDictionary()
addEntry(LLFolderType::FT_OUTBOX, new FolderEntry("outbox", FALSE));
addEntry(LLFolderType::FT_BASIC_ROOT, new FolderEntry("basic_rt", TRUE));
+
+ addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE));
+ addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new FolderEntry("stock", FALSE));
+ addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE));
addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE));
};
diff --git a/indra/llcommon/llfoldertype.h b/indra/llcommon/llfoldertype.h
index a0c847914f..515bb05a3f 100644
--- a/indra/llcommon/llfoldertype.h
+++ b/indra/llcommon/llfoldertype.h
@@ -87,6 +87,10 @@ public:
FT_BASIC_ROOT = 52,
+ FT_MARKETPLACE_LISTINGS = 53,
+ FT_MARKETPLACE_STOCK = 54,
+ FT_MARKETPLACE_VERSION = 55, // Note: We actually *never* create folders with that type. This is used for icon override only.
+
FT_COUNT,
FT_NONE = -1
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 0bf0152b30..fdc8bfbf04 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -245,6 +245,7 @@ set(viewer_SOURCE_FILES
llfloaterlagmeter.cpp
llfloaterland.cpp
llfloaterlandholdings.cpp
+ llfloatermarketplacelistings.cpp
llfloatermap.cpp
llfloatermediasettings.cpp
llfloatermemleak.cpp
@@ -835,6 +836,7 @@ set(viewer_HEADER_FILES
llfloaterland.h
llfloaterlandholdings.h
llfloatermap.h
+ llfloatermarketplacelistings.h
llfloatermediasettings.h
llfloatermemleak.h
llfloatermodelpreview.h
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 60c942094a..fd74166980 100755
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -118,6 +118,16 @@
tooltip_ref="Command_Marketplace_Tooltip"
execute_function="Avatar.OpenMarketplace"
/>
+ <command name="marketplacelistings"
+ available_in_toybox="true"
+ icon="Command_Marketplace_Icon"
+ label_ref="Command_MarketplaceListings_Label"
+ tooltip_ref="Command_MarketplaceListings_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="marketplace_listings"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="marketplace_listings"
+ />
<command name="minimap"
available_in_toybox="true"
icon="Command_MiniMap_Icon"
diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml
index 86f9912815..69cae1b945 100755
--- a/indra/newview/app_settings/toolbars.xml
+++ b/indra/newview/app_settings/toolbars.xml
@@ -22,5 +22,6 @@
<command name="voice"/>
<command name="minimap"/>
<command name="snapshot"/>
+ <command name="marketplacelistings"/>
</left_toolbar>
</toolbars>
diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp
new file mode 100755
index 0000000000..76cca065c8
--- /dev/null
+++ b/indra/newview/llfloatermarketplacelistings.cpp
@@ -0,0 +1,671 @@
+/**
+ * @file llfloatermarketplacelistings.cpp
+ * @brief Implementation of the marketplace listings floater and panels
+ * @author merov@lindenlab.com
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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 "llfloatermarketplacelistings.h"
+
+#include "llfloaterreg.h"
+#include "llfolderview.h"
+#include "llinventorybridge.h"
+#include "llinventorymodelbackgroundfetch.h"
+#include "llinventoryobserver.h"
+#include "llinventoryfunctions.h"
+#include "llmarketplacefunctions.h"
+#include "llnotificationhandler.h"
+#include "llnotificationmanager.h"
+#include "llnotificationsutil.h"
+#include "llsidepaneliteminfo.h"
+#include "lltextbox.h"
+#include "lltrans.h"
+
+///----------------------------------------------------------------------------
+/// LLPanelMarketplaceListings
+///----------------------------------------------------------------------------
+
+static LLPanelInjector<LLPanelMarketplaceListings> t_panel_status("llpanelmarketplacelistings");
+
+LLPanelMarketplaceListings::LLPanelMarketplaceListings()
+: mAllPanel(NULL)
+, mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME)
+, mFilterType(LLInventoryFilter::FILTERTYPE_NONE)
+{
+ mCommitCallbackRegistrar.add("Marketplace.ViewSort.Action", boost::bind(&LLPanelMarketplaceListings::onViewSortMenuItemClicked, this, _2));
+ mEnableCallbackRegistrar.add("Marketplace.ViewSort.CheckItem", boost::bind(&LLPanelMarketplaceListings::onViewSortMenuItemCheck, this, _2));
+}
+
+BOOL LLPanelMarketplaceListings::postBuild()
+{
+ mAllPanel = getChild<LLInventoryPanel>("All Items");
+ childSetAction("add_btn", boost::bind(&LLPanelMarketplaceListings::onAddButtonClicked, this));
+ childSetAction("audit_btn", boost::bind(&LLPanelMarketplaceListings::onAuditButtonClicked, this));
+
+ // Set the sort order newest to oldest
+ LLInventoryPanel* panel = getChild<LLInventoryPanel>("All Items");
+ panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ panel->getFilter().markDefault();
+ panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2));
+
+ // Set filters on the 3 prefiltered panels
+ panel = getChild<LLInventoryPanel>("Active Items");
+ panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ panel->getFilter().setFilterMarketplaceActiveFolders();
+ panel->getFilter().markDefault();
+ panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2));
+ panel = getChild<LLInventoryPanel>("Inactive Items");
+ panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ panel->getFilter().setFilterMarketplaceInactiveFolders();
+ panel->getFilter().markDefault();
+ panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2));
+ panel = getChild<LLInventoryPanel>("Unassociated Items");
+ panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ panel->getFilter().setFilterMarketplaceUnassociatedFolders();
+ panel->getFilter().markDefault();
+ panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2));
+
+ return LLPanel::postBuild();
+}
+
+void LLPanelMarketplaceListings::draw()
+{
+ LLPanel::draw();
+}
+
+void LLPanelMarketplaceListings::onSelectionChange(LLInventoryPanel *panel, const std::deque<LLFolderViewItem*>& items, BOOL user_action)
+{
+ panel->onSelectionChange(items, user_action);
+}
+
+void LLPanelMarketplaceListings::onAddButtonClicked()
+{
+ LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ llassert(marketplacelistings_id.notNull());
+ LLFolderType::EType preferred_type = LLFolderType::lookup("category");
+ LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null);
+ gInventory.notifyObservers();
+ mAllPanel->setSelectionByID(category, TRUE);
+}
+
+void LLPanelMarketplaceListings::onAuditButtonClicked()
+{
+ LLSD data(LLSD::emptyMap());
+ LLFloaterReg::showInstance("marketplace_validation", data);
+}
+
+void LLPanelMarketplaceListings::onViewSortMenuItemClicked(const LLSD& userdata)
+{
+ std::string chosen_item = userdata.asString();
+
+ // Sort options
+ if (chosen_item == "sort_by_stock_amount")
+ {
+ mSortOrder = (mSortOrder == LLInventoryFilter::SO_FOLDERS_BY_NAME ? LLInventoryFilter::SO_FOLDERS_BY_WEIGHT : LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ mAllPanel->setSortOrder(mSortOrder);
+ }
+}
+
+bool LLPanelMarketplaceListings::onViewSortMenuItemCheck(const LLSD& userdata)
+{
+ std::string chosen_item = userdata.asString();
+
+ if (chosen_item == "sort_by_stock_amount")
+ return mSortOrder == LLInventoryFilter::SO_FOLDERS_BY_WEIGHT;
+ return false;
+}
+
+///----------------------------------------------------------------------------
+/// LLMarketplaceListingsAddedObserver helper class
+///----------------------------------------------------------------------------
+
+class LLMarketplaceListingsAddedObserver : public LLInventoryCategoryAddedObserver
+{
+public:
+ LLMarketplaceListingsAddedObserver(LLFloaterMarketplaceListings * marketplace_listings_floater)
+ : LLInventoryCategoryAddedObserver()
+ , mMarketplaceListingsFloater(marketplace_listings_floater)
+ {
+ }
+
+ void done()
+ {
+ for (cat_vec_t::iterator it = mAddedCategories.begin(); it != mAddedCategories.end(); ++it)
+ {
+ LLViewerInventoryCategory* added_category = *it;
+
+ LLFolderType::EType added_category_type = added_category->getPreferredType();
+
+ if (added_category_type == LLFolderType::FT_MARKETPLACE_LISTINGS)
+ {
+ mMarketplaceListingsFloater->initializeMarketPlace();
+ }
+ }
+ }
+
+private:
+ LLFloaterMarketplaceListings * mMarketplaceListingsFloater;
+};
+
+///----------------------------------------------------------------------------
+/// LLFloaterMarketplaceListings
+///----------------------------------------------------------------------------
+
+LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key)
+: LLFloater(key)
+, mCategoriesObserver(NULL)
+, mCategoryAddedObserver(NULL)
+, mRootFolderId(LLUUID::null)
+, mInventoryStatus(NULL)
+, mInventoryInitializationInProgress(NULL)
+, mInventoryPlaceholder(NULL)
+, mInventoryText(NULL)
+, mInventoryTitle(NULL)
+, mPanelListings(NULL)
+{
+}
+
+LLFloaterMarketplaceListings::~LLFloaterMarketplaceListings()
+{
+ if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))
+ {
+ gInventory.removeObserver(mCategoriesObserver);
+ }
+ delete mCategoriesObserver;
+
+ if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver))
+ {
+ gInventory.removeObserver(mCategoryAddedObserver);
+ }
+ delete mCategoryAddedObserver;
+}
+
+BOOL LLFloaterMarketplaceListings::postBuild()
+{
+ mInventoryStatus = getChild<LLTextBox>("marketplace_status");
+ mInventoryInitializationInProgress = getChild<LLView>("initialization_progress_indicator");
+ mInventoryPlaceholder = getChild<LLView>("marketplace_listings_inventory_placeholder_panel");
+ mInventoryText = mInventoryPlaceholder->getChild<LLTextBox>("marketplace_listings_inventory_placeholder_text");
+ mInventoryTitle = mInventoryPlaceholder->getChild<LLTextBox>("marketplace_listings_inventory_placeholder_title");
+
+ mPanelListings = static_cast<LLPanelMarketplaceListings*>(getChild<LLUICtrl>("panel_marketplace_listing"));
+
+ LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLFloaterMarketplaceListings::onFocusReceived, this));
+
+ // Observe category creation to catch marketplace listings creation (moot if already existing)
+ mCategoryAddedObserver = new LLMarketplaceListingsAddedObserver(this);
+ gInventory.addObserver(mCategoryAddedObserver);
+
+ // Merov : Debug : fetch aggressively so we can create test data right onOpen()
+ llinfos << "Merov : postBuild, do fetchContent() ahead of time" << llendl;
+ fetchContents();
+
+ return TRUE;
+}
+
+void LLFloaterMarketplaceListings::onClose(bool app_quitting)
+{
+}
+
+void LLFloaterMarketplaceListings::onOpen(const LLSD& key)
+{
+ //
+ // Initialize the Market Place or go update the marketplace listings
+ //
+ if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED)
+ {
+ initializeMarketPlace();
+ }
+ else
+ {
+ setup();
+ }
+
+ //
+ // Update the floater view
+ //
+ updateView();
+
+ //
+ // Trigger fetch of the contents
+ //
+ fetchContents();
+}
+
+void LLFloaterMarketplaceListings::onFocusReceived()
+{
+ fetchContents();
+}
+
+void LLFloaterMarketplaceListings::fetchContents()
+{
+ if (mRootFolderId.notNull())
+ {
+ LLInventoryModelBackgroundFetch::instance().start(mRootFolderId);
+ }
+}
+
+void LLFloaterMarketplaceListings::setup()
+{
+ if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT)
+ {
+ // If we are *not* a merchant or we have no market place connection established yet, do nothing
+ return;
+ }
+
+ // We are a merchant. Get the Marketplace listings folder, create it if needs be.
+ LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true);
+ if (marketplacelistings_id.isNull())
+ {
+ // We should never get there unless the inventory fails badly
+ llinfos << "Merov : Inventory problem: failure to create the marketplace listings folder for a merchant!" << llendl;
+ llerrs << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << llendl;
+ return;
+ }
+
+ // Consolidate Marketplace listings
+ // We shouldn't have to do that but with a client/server system relying on a "well known folder" convention, things get messy and conventions get broken down eventually
+ gInventory.consolidateForType(marketplacelistings_id, LLFolderType::FT_MARKETPLACE_LISTINGS);
+
+ if (marketplacelistings_id == mRootFolderId)
+ {
+ llinfos << "Merov : Inventory warning: Marketplace listings folder already set" << llendl;
+ llwarns << "Inventory warning: Marketplace listings folder already set" << llendl;
+ return;
+ }
+ mRootFolderId = marketplacelistings_id;
+
+ // No longer need to observe new category creation
+ if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver))
+ {
+ gInventory.removeObserver(mCategoryAddedObserver);
+ delete mCategoryAddedObserver;
+ mCategoryAddedObserver = NULL;
+ }
+ llassert(!mCategoryAddedObserver);
+
+ // Create observer for marketplace listings modifications : clear the old one and create a new one
+ if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))
+ {
+ gInventory.removeObserver(mCategoriesObserver);
+ delete mCategoriesObserver;
+ }
+ mCategoriesObserver = new LLInventoryCategoriesObserver();
+ gInventory.addObserver(mCategoriesObserver);
+ mCategoriesObserver->addCategory(mRootFolderId, boost::bind(&LLFloaterMarketplaceListings::onChanged, this));
+ llassert(mCategoriesObserver);
+
+ // Get the content of the marketplace listings folder
+ fetchContents();
+}
+
+void LLFloaterMarketplaceListings::initializeMarketPlace()
+{
+ //
+ // Initialize the marketplace import API
+ //
+ LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance();
+
+ if (!importer.isInitialized())
+ {
+ importer.setInitializationErrorCallback(boost::bind(&LLFloaterMarketplaceListings::initializationReportError, this, _1, _2));
+ importer.setStatusChangedCallback(boost::bind(&LLFloaterMarketplaceListings::importStatusChanged, this, _1));
+ importer.setStatusReportCallback(boost::bind(&LLFloaterMarketplaceListings::importReportResults, this, _1, _2));
+ importer.initialize();
+ }
+}
+
+S32 LLFloaterMarketplaceListings::getFolderCount()
+{
+ if (mPanelListings && mRootFolderId.notNull())
+ {
+ LLInventoryModel::cat_array_t * cats;
+ LLInventoryModel::item_array_t * items;
+ gInventory.getDirectDescendentsOf(mRootFolderId, cats, items);
+
+ return (cats->count() + items->count());
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+void LLFloaterMarketplaceListings::setStatusString(const std::string& statusString)
+{
+ mInventoryStatus->setText(statusString);
+}
+
+void LLFloaterMarketplaceListings::updateView()
+{
+ if (getFolderCount() > 0)
+ {
+ mPanelListings->setVisible(TRUE);
+ mInventoryPlaceholder->setVisible(FALSE);
+ }
+ else
+ {
+ mPanelListings->setVisible(FALSE);
+ mInventoryPlaceholder->setVisible(TRUE);
+
+ std::string text;
+ std::string title;
+ std::string tooltip;
+
+ const LLSD& subs = getMarketplaceStringSubstitutions();
+ U32 mkt_status = LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus();
+
+ // *TODO : check those messages and create better appropriate ones in strings.xml
+ if (mRootFolderId.notNull())
+ {
+ // Does the marketplace listings folder needs recreation?
+ if (!mPanelListings || !gInventory.getCategory(mRootFolderId))
+ {
+ setup();
+ }
+ // "Marketplace listings is empty!" message strings
+ text = LLTrans::getString("InventoryMarketplaceListingsNoItems", subs);
+ title = LLTrans::getString("InventoryMarketplaceListingsNoItemsTitle");
+ tooltip = LLTrans::getString("InventoryMarketplaceListingsNoItemsTooltip");
+ }
+ else if (mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING)
+ {
+ // "Initializing!" message strings
+ text = LLTrans::getString("InventoryOutboxInitializing", subs);
+ title = LLTrans::getString("InventoryOutboxInitializingTitle");
+ tooltip = LLTrans::getString("InventoryOutboxInitializingTooltip");
+ }
+ else if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT)
+ {
+ // "Not a merchant!" message strings
+ text = LLTrans::getString("InventoryOutboxNotMerchant", subs);
+ title = LLTrans::getString("InventoryOutboxNotMerchantTitle");
+ tooltip = LLTrans::getString("InventoryOutboxNotMerchantTooltip");
+ }
+ else
+ {
+ // "Errors!" message strings
+ text = LLTrans::getString("InventoryOutboxError", subs);
+ title = LLTrans::getString("InventoryOutboxErrorTitle");
+ tooltip = LLTrans::getString("InventoryOutboxErrorTooltip");
+ }
+
+ mInventoryText->setValue(text);
+ mInventoryTitle->setValue(title);
+ mInventoryPlaceholder->getParent()->setToolTip(tooltip);
+ }
+}
+
+bool LLFloaterMarketplaceListings::isAccepted(EAcceptance accept)
+{
+ return (accept >= ACCEPT_YES_COPY_SINGLE);
+}
+
+BOOL LLFloaterMarketplaceListings::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // If there's no panel to accept drops or no existing marketplace listings folder, we refuse all drop
+ if (!mPanelListings || mRootFolderId.isNull())
+ {
+ return FALSE;
+ }
+
+ // Pass to the children
+ LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+ BOOL handled = (handled_view != NULL);
+
+ // If no one handled it or it was not accepted, we try to accept it at the floater level as if it was dropped on the
+ // marketplace listings root folder
+ if (!handled || !isAccepted(*accept))
+ {
+ if (!mPanelListings->getVisible() && mRootFolderId.notNull())
+ {
+ LLFolderView* root_folder = mPanelListings->getRootFolder();
+ handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLFloaterMarketplaceListings::handleHover(S32 x, S32 y, MASK mask)
+{
+ return LLFloater::handleHover(x, y, mask);
+}
+
+void LLFloaterMarketplaceListings::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ LLFloater::onMouseLeave(x, y, mask);
+}
+
+void LLFloaterMarketplaceListings::onChanged()
+{
+ LLViewerInventoryCategory* category = gInventory.getCategory(mRootFolderId);
+ if (mRootFolderId.notNull() && category)
+ {
+ fetchContents();
+ updateView();
+ }
+ else
+ {
+ // Invalidate the marketplace listings data
+ mRootFolderId.setNull();
+ }
+}
+
+void LLFloaterMarketplaceListings::initializationReportError(U32 status, const LLSD& content)
+{
+ updateView();
+}
+
+void LLFloaterMarketplaceListings::importStatusChanged(bool inProgress)
+{
+ if (mRootFolderId.isNull() && (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT))
+ {
+ setup();
+ }
+
+ if (inProgress)
+ {
+ setStatusString(getString("MarketplaceListingsInitializing"));
+ mInventoryInitializationInProgress->setVisible(true);
+ }
+ else
+ {
+ setStatusString("");
+ mInventoryInitializationInProgress->setVisible(false);
+ }
+
+ updateView();
+}
+
+void LLFloaterMarketplaceListings::importReportResults(U32 status, const LLSD& content)
+{
+ updateView();
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterAssociateListing
+//-----------------------------------------------------------------------------
+
+LLFloaterAssociateListing::LLFloaterAssociateListing(const LLSD& key)
+: LLFloater(key)
+, mUUID()
+{
+}
+
+LLFloaterAssociateListing::~LLFloaterAssociateListing()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+}
+
+BOOL LLFloaterAssociateListing::postBuild()
+{
+ getChild<LLButton>("OK")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::apply, this));
+ getChild<LLButton>("Cancel")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::cancel, this));
+ center();
+
+ return LLFloater::postBuild();
+}
+
+BOOL LLFloaterAssociateListing::handleKeyHere(KEY key, MASK mask)
+{
+ if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ apply();
+ return TRUE;
+ }
+ else if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ cancel();
+ return TRUE;
+ }
+
+ return LLFloater::handleKeyHere(key, mask);
+}
+
+// static
+LLFloaterAssociateListing* LLFloaterAssociateListing::show(const LLUUID& folder_id)
+{
+ LLFloaterAssociateListing* floater = LLFloaterReg::showTypedInstance<LLFloaterAssociateListing>("associate_listing");
+
+ floater->mUUID = folder_id;
+
+ return floater;
+}
+
+void LLFloaterAssociateListing::apply()
+{
+ if (mUUID.notNull())
+ {
+ S32 id = (S32)getChild<LLUICtrl>("listing_id")->getValue().asInteger();
+ if (id > 0)
+ {
+ LLMarketplaceData::instance().associateListing(mUUID,id);
+ }
+ }
+ closeFloater();
+}
+
+void LLFloaterAssociateListing::cancel()
+{
+ closeFloater();
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterMarketplaceValidation
+//-----------------------------------------------------------------------------
+
+LLFloaterMarketplaceValidation::LLFloaterMarketplaceValidation(const LLSD& key)
+: LLFloater(key),
+mEditor(NULL)
+{
+}
+
+BOOL LLFloaterMarketplaceValidation::postBuild()
+{
+ childSetAction("OK", onOK, this);
+
+ // This widget displays the validation messages
+ mEditor = getChild<LLTextEditor>("validation_text");
+ mEditor->setEnabled(FALSE);
+ mEditor->setFocus(TRUE);
+ mEditor->setValue(LLSD());
+
+ return TRUE;
+}
+
+LLFloaterMarketplaceValidation::~LLFloaterMarketplaceValidation()
+{
+}
+
+// virtual
+void LLFloaterMarketplaceValidation::draw()
+{
+ // draw children
+ LLFloater::draw();
+}
+
+void LLFloaterMarketplaceValidation::onOpen(const LLSD& key)
+{
+ // Clear the text panel
+ mEditor->setValue(LLSD());
+
+ // Validates the marketplace
+ LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true);
+ llassert(marketplacelistings_id.notNull());
+ LLViewerInventoryCategory* cat = gInventory.getCategory(marketplacelistings_id);
+ validate_marketplacelistings(cat,boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1));
+}
+
+// static
+void LLFloaterMarketplaceValidation::onOK( void* userdata )
+{
+ // destroys this object
+ LLFloaterMarketplaceValidation* self = (LLFloaterMarketplaceValidation*) userdata;
+ self->closeFloater();
+}
+
+void LLFloaterMarketplaceValidation::appendMessage(std::string& message)
+{
+ if (mEditor)
+ {
+ mEditor->appendText(message, true);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterItemProperties
+//-----------------------------------------------------------------------------
+
+LLFloaterItemProperties::LLFloaterItemProperties(const LLSD& key)
+: LLFloater(key)
+{
+}
+
+LLFloaterItemProperties::~LLFloaterItemProperties()
+{
+}
+
+BOOL LLFloaterItemProperties::postBuild()
+{
+ // On the standalone properties floater, we have no need for a back button...
+ LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel");
+ LLButton* back_btn = panel->getChild<LLButton>("back_btn");
+ back_btn->setVisible(FALSE);
+
+ return LLFloater::postBuild();
+}
+
+void LLFloaterItemProperties::onOpen(const LLSD& key)
+{
+ // Tell the panel which item it needs to visualize
+ LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel");
+ panel->setItemID(key["id"].asUUID());
+}
+
diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h
new file mode 100755
index 0000000000..cb900c903c
--- /dev/null
+++ b/indra/newview/llfloatermarketplacelistings.h
@@ -0,0 +1,190 @@
+/**
+ * @file llfloatermarketplacelistings.h
+ * @brief Implementation of the marketplace listings floater and panels
+ * @author merov@lindenlab.com
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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
+ * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERMARKETPLACELISTINGS_H
+#define LL_LLFLOATERMARKETPLACELISTINGS_H
+
+#include "llfloater.h"
+#include "llinventoryfilter.h"
+#include "llinventorypanel.h"
+#include "llnotificationptr.h"
+#include "llmodaldialog.h"
+#include "lltexteditor.h"
+
+class LLInventoryCategoriesObserver;
+class LLInventoryCategoryAddedObserver;
+class LLTextBox;
+class LLView;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPanelMarketplaceListings
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLPanelMarketplaceListings : public LLPanel
+{
+public:
+ LLPanelMarketplaceListings();
+ BOOL postBuild();
+ void draw();
+ LLFolderView* getRootFolder() { return mAllPanel->getRootFolder(); } // *TODO : Suppress and get DnD in here instead...
+
+private:
+ // UI callbacks
+ void onViewSortMenuItemClicked(const LLSD& userdata);
+ bool onViewSortMenuItemCheck(const LLSD& userdata);
+ void onAddButtonClicked();
+ void onAuditButtonClicked();
+ void onSelectionChange(LLInventoryPanel *panel, const std::deque<LLFolderViewItem*>& items, BOOL user_action);
+
+ LLInventoryPanel* mAllPanel;
+ LLInventoryFilter::ESortOrderType mSortOrder;
+ LLInventoryFilter::EFilterType mFilterType;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterMarketplaceListings
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterMarketplaceListings : public LLFloater
+{
+public:
+ LLFloaterMarketplaceListings(const LLSD& key);
+ ~LLFloaterMarketplaceListings();
+
+ void initializeMarketPlace();
+
+ // virtuals
+ BOOL postBuild();
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+ void showNotification(const LLNotificationPtr& notification);
+
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ void onMouseLeave(S32 x, S32 y, MASK mask);
+
+protected:
+ void setup();
+ void fetchContents();
+
+ void importReportResults(U32 status, const LLSD& content);
+ void importStatusChanged(bool inProgress);
+ void initializationReportError(U32 status, const LLSD& content);
+ void setStatusString(const std::string& statusString);
+
+ void onClose(bool app_quitting);
+ void onOpen(const LLSD& key);
+ void onFocusReceived();
+ void onChanged();
+
+ bool isAccepted(EAcceptance accept);
+
+ void updateView();
+
+private:
+ S32 getFolderCount();
+
+ LLInventoryCategoriesObserver * mCategoriesObserver;
+ LLInventoryCategoryAddedObserver * mCategoryAddedObserver;
+
+ LLTextBox * mInventoryStatus;
+ LLView * mInventoryInitializationInProgress;
+ LLView * mInventoryPlaceholder;
+ LLTextBox * mInventoryText;
+ LLTextBox * mInventoryTitle;
+
+ LLUUID mRootFolderId;
+ LLPanelMarketplaceListings * mPanelListings;
+};
+
+//-----------------------------------------------------------------------------
+// LLFloaterAssociateListing
+//-----------------------------------------------------------------------------
+class LLFloaterAssociateListing : public LLFloater
+{
+ friend class LLFloaterReg;
+public:
+ virtual BOOL postBuild();
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ static LLFloaterAssociateListing* show(const LLUUID& folder_id);
+
+private:
+ LLFloaterAssociateListing(const LLSD& key);
+ virtual ~LLFloaterAssociateListing();
+
+ // UI Callbacks
+ void apply();
+ void cancel();
+
+ LLUUID mUUID;
+};
+
+//-----------------------------------------------------------------------------
+// LLFloaterMarketplaceValidation
+//-----------------------------------------------------------------------------
+// Note: For the moment, we just display the validation text. Eventually, we should
+// get the validation triggered on the server and display the html report.
+// *TODO : morph into an html/text window using the pattern in llfloatertos
+
+class LLFloaterMarketplaceValidation : public LLFloater
+{
+public:
+ LLFloaterMarketplaceValidation(const LLSD& key);
+ virtual ~LLFloaterMarketplaceValidation();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void onOpen(const LLSD& key);
+
+ void appendMessage(std::string& message);
+ static void onOK( void* userdata );
+
+private:
+ LLTextEditor* mEditor;
+};
+
+//-----------------------------------------------------------------------------
+// LLFloaterItemProperties
+//-----------------------------------------------------------------------------
+
+class LLFloaterItemProperties : public LLFloater
+{
+public:
+ LLFloaterItemProperties(const LLSD& key);
+ virtual ~LLFloaterItemProperties();
+
+ BOOL postBuild();
+ virtual void onOpen(const LLSD& key);
+
+private:
+};
+
+#endif // LL_LLFLOATERMARKETPLACELISTINGS_H
diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp
index de96f75602..f5ebd5cf51 100755
--- a/indra/newview/llfloateroutbox.cpp
+++ b/indra/newview/llfloateroutbox.cpp
@@ -617,3 +617,5 @@ void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification)
notification_handler->processNotification(notification);
}
+
+
diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h
index 40519c8fd2..2cf69fc3cc 100755
--- a/indra/newview/llfloateroutbox.h
+++ b/indra/newview/llfloateroutbox.h
@@ -1,7 +1,6 @@
/**
* @file llfloateroutbox.h
- * @brief LLFloaterOutbox
- * class definition
+ * @brief Implementation of the merchant outbox window
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -30,6 +29,7 @@
#include "llfloater.h"
#include "llfoldertype.h"
+#include "llinventoryfilter.h"
#include "llnotificationptr.h"
diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp
index aac3a41b9e..c31b40b179 100755
--- a/indra/newview/llfolderviewmodelinventory.cpp
+++ b/indra/newview/llfolderviewmodelinventory.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llfolderviewmodelinventory.h"
#include "llinventorymodelbackgroundfetch.h"
+#include "llinventoryfunctions.h"
#include "llinventorypanel.h"
#include "lltooldraganddrop.h"
#include "llfavoritesbar.h"
@@ -269,7 +270,7 @@ bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a,
// We sort by name if we aren't sorting by date
// OR if these are folders and we are sorting folders by name.
- bool by_name = (!mByDate || (mFoldersByName && (a->getSortGroup() != SG_ITEM)));
+ bool by_name = ((!mByDate || (mFoldersByName && (a->getSortGroup() != SG_ITEM))) && !mFoldersByWeight);
if (a->getSortGroup() != b->getSortGroup())
{
@@ -301,6 +302,35 @@ bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a,
return (compare < 0);
}
}
+ else if (mFoldersByWeight)
+ {
+ S32 weight_a = compute_stock_count(a->getUUID());
+ S32 weight_b = compute_stock_count(b->getUUID());
+ if ((weight_a != -1) || (weight_b != -1))
+ {
+ llinfos << "Merov : sort by weight, a = " << a->getName() << ", " << weight_a << ", b = " << b->getName() << ", " << weight_b << llendl;
+ }
+ if (weight_a == weight_b)
+ {
+ // Equal weight -> use alphabetical order
+ return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0);
+ }
+ else if (weight_a == -1)
+ {
+ // No weight -> move a at the end of the list
+ return false;
+ }
+ else if (weight_b == -1)
+ {
+ // No weight -> move b at the end of the list
+ return true;
+ }
+ else
+ {
+ // Lighter is first (sorted in increasing order of weight)
+ return (weight_a < weight_b);
+ }
+ }
else
{
time_t first_create = a->getCreationDate();
diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h
index 9dcfdfa185..b6d2c8502b 100755
--- a/indra/newview/llfolderviewmodelinventory.h
+++ b/indra/newview/llfolderviewmodelinventory.h
@@ -89,6 +89,7 @@ public:
mByDate = (mSortOrder & LLInventoryFilter::SO_DATE);
mSystemToTop = (mSortOrder & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP);
mFoldersByName = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ mFoldersByWeight = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_WEIGHT);
}
bool operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const;
@@ -97,6 +98,7 @@ private:
bool mByDate;
bool mSystemToTop;
bool mFoldersByName;
+ bool mFoldersByWeight;
};
class LLFolderViewModelInventory
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 44943d8722..e6ecd4e96e 100755
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -40,6 +40,7 @@
#include "llfavoritesbar.h" // management of favorites folder
#include "llfloateropenobject.h"
#include "llfloaterreg.h"
+#include "llfloatermarketplacelistings.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloaterworldmap.h"
#include "llfolderview.h"
@@ -65,6 +66,7 @@
#include "llsidepanelappearance.h"
#include "lltooldraganddrop.h"
#include "lltrans.h"
+#include "llurlaction.h"
#include "llviewerassettype.h"
#include "llviewerfoldertype.h"
#include "llviewermenu.h"
@@ -288,7 +290,15 @@ BOOL LLInvFVBridge::copyToClipboard() const
// *TODO: make sure this does the right thing
void LLInvFVBridge::showProperties()
{
- show_item_profile(mUUID);
+ LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true);
+ if (gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id))
+ {
+ LLFloaterReg::showInstance("item_properties", LLSD().with("id",mUUID));
+ }
+ else
+ {
+ show_item_profile(mUUID);
+ }
// Disable old properties floater; this is replaced by the sidepanel.
/*
@@ -650,7 +660,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
if (!isInboxFolder())
{
items.push_back(std::string("Rename"));
- if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0))
{
disabled_items.push_back(std::string("Rename"));
}
@@ -688,7 +698,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
disabled_items.push_back(std::string("Cut"));
}
- if (canListOnMarketplace())
+ if (canListOnMarketplace() && !isMarketplaceListingsFolder())
{
items.push_back(std::string("Marketplace Separator"));
@@ -845,6 +855,81 @@ void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags,
#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU
}
+void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags,
+ menuentry_vec_t &items,
+ menuentry_vec_t &disabled_items)
+{
+ S32 depth = depth_nesting_in_marketplace(mUUID);
+ if (depth == 1)
+ {
+ // Options available at the Listing Folder level
+ items.push_back(std::string("Marketplace Create Listing"));
+ items.push_back(std::string("Marketplace Associate Listing"));
+ items.push_back(std::string("Marketplace Disassociate Listing"));
+ items.push_back(std::string("Marketplace List"));
+ items.push_back(std::string("Marketplace Unlist"));
+ if (LLMarketplaceData::instance().isListed(mUUID))
+ {
+ disabled_items.push_back(std::string("Marketplace Create Listing"));
+ disabled_items.push_back(std::string("Marketplace Associate Listing"));
+ if (LLMarketplaceData::instance().getVersionFolderID(mUUID).isNull())
+ {
+ disabled_items.push_back(std::string("Marketplace List"));
+ disabled_items.push_back(std::string("Marketplace Unlist"));
+ }
+ else
+ {
+ if (LLMarketplaceData::instance().getActivationState(mUUID))
+ {
+ disabled_items.push_back(std::string("Marketplace List"));
+ }
+ else
+ {
+ disabled_items.push_back(std::string("Marketplace Unlist"));
+ }
+ }
+ }
+ else
+ {
+ disabled_items.push_back(std::string("Marketplace Disassociate Listing"));
+ disabled_items.push_back(std::string("Marketplace List"));
+ disabled_items.push_back(std::string("Marketplace Unlist"));
+ }
+ }
+ if (depth == 2)
+ {
+ // Options available at the Version Folder levels and only for folders
+ LLInventoryCategory* cat = gInventory.getCategory(mUUID);
+ if (cat && LLMarketplaceData::instance().isListed(cat->getParentUUID()))
+ {
+ items.push_back(std::string("Marketplace Activate"));
+ items.push_back(std::string("Marketplace Deactivate"));
+ if (LLMarketplaceData::instance().isVersionFolder(mUUID))
+ {
+ disabled_items.push_back(std::string("Marketplace Activate"));
+ if (LLMarketplaceData::instance().getActivationState(mUUID))
+ {
+ disabled_items.push_back(std::string("Marketplace Deactivate"));
+ }
+ }
+ else
+ {
+ disabled_items.push_back(std::string("Marketplace Deactivate"));
+ }
+ }
+ }
+ // Options available at all levels on items and categories
+ items.push_back(std::string("Marketplace Edit Listing"));
+ LLUUID listing_folder_id = nested_parent_id(mUUID,depth);
+ if (!LLMarketplaceData::instance().isListed(listing_folder_id))
+ {
+ disabled_items.push_back(std::string("Marketplace Edit Listing"));
+ }
+ // Separator
+ items.push_back(std::string("Marketplace Listings Separator"));
+}
+
+
// *TODO: remove this
BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const
{
@@ -959,6 +1044,18 @@ BOOL LLInvFVBridge::isInboxFolder() const
return gInventory.isObjectDescendentOf(mUUID, inbox_id);
}
+BOOL LLInvFVBridge::isMarketplaceListingsFolder() const
+{
+ const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+
+ if (folder_id.isNull())
+ {
+ return FALSE;
+ }
+
+ return gInventory.isObjectDescendentOf(mUUID, folder_id);
+}
+
BOOL LLInvFVBridge::isOutboxFolder() const
{
const LLUUID outbox_id = getOutboxFolder();
@@ -1174,6 +1271,22 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
}
}
+void LLInvFVBridge::removeObject(LLInventoryModel *model, const LLUUID &uuid)
+{
+ // Keep track of the parent
+ LLInventoryItem* itemp = model->getItem(uuid);
+ LLUUID parent_id = (itemp ? itemp->getParentUUID() : LLUUID::null);
+ // Remove the object
+ model->removeObject(uuid);
+ // Get the parent updated
+ if (parent_id.notNull())
+ {
+ LLViewerInventoryCategory* parent_cat = model->getCategory(parent_id);
+ model->updateCategory(parent_cat);
+ model->notifyObservers();
+ }
+}
+
bool LLInvFVBridge::canShare() const
{
bool can_share = false;
@@ -1394,7 +1507,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
else if ("cut" == action)
{
cutToClipboard();
- gInventory.removeObject(mUUID);
+ removeObject(model, mUUID);
return;
}
else if ("copy" == action)
@@ -1454,6 +1567,11 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
{
doActionOnCurSelectedLandmark(boost::bind(&LLItemBridge::doShowOnMap, this, _1));
}
+ else if ("marketplace_edit_listing" == action)
+ {
+ std::string url = LLMarketplaceData::instance().getListingURL(mUUID);
+ LLUrlAction::openURL(url);
+ }
}
void LLItemBridge::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb)
@@ -1931,6 +2049,67 @@ void LLFolderBridge::buildDisplayName() const
}
}
+std::string LLFolderBridge::getLabelSuffix() const
+{
+ if (isMarketplaceListingsFolder())
+ {
+ std::string suffix = "";
+ // Listing folder case
+ if (LLMarketplaceData::instance().isListed(getUUID()))
+ {
+ suffix = llformat("%d",LLMarketplaceData::instance().getListingID(getUUID()));
+ if (suffix.empty())
+ {
+ suffix = LLTrans::getString("MarketplaceNoID");
+ }
+ suffix = " (" + suffix + ")";
+ if (LLMarketplaceData::instance().getActivationState(getUUID()))
+ {
+ suffix += " (" + LLTrans::getString("MarketplaceLive") + ")";
+ }
+ }
+ // Version folder case
+ else if (LLMarketplaceData::instance().isVersionFolder(getUUID()))
+ {
+ suffix += " (" + LLTrans::getString("MarketplaceActive") + ")";
+ }
+ // Add stock amount
+ S32 stock_count = compute_stock_count(getUUID());
+ if (stock_count == 0)
+ {
+ suffix += " (" + LLTrans::getString("MarketplaceNoStock") + ")";
+ }
+ else if (stock_count != -1)
+ {
+ if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
+ {
+ suffix += " (" + LLTrans::getString("MarketplaceStock") + "=" + llformat("%d", stock_count) + ")";
+ }
+ else
+ {
+ suffix += " (" + LLTrans::getString("MarketplaceMax") + "=" + llformat("%d", stock_count) + ")";
+ }
+ }
+ return LLInvFVBridge::getLabelSuffix() + suffix;
+ }
+ else
+ {
+ return LLInvFVBridge::getLabelSuffix();
+ }
+}
+
+LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const
+{
+ if (isMarketplaceListingsFolder() && LLMarketplaceData::instance().getActivationState(getUUID()))
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
void LLFolderBridge::update()
{
@@ -2017,6 +2196,11 @@ BOOL LLFolderBridge::isItemRemovable() const
return FALSE;
}
}
+
+ if (isMarketplaceListingsFolder() && LLMarketplaceData::instance().getActivationState(mUUID))
+ {
+ return FALSE;
+ }
return TRUE;
}
@@ -2252,10 +2436,14 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
const LLUUID &cat_id = inv_cat->getUUID();
const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID from_folder_uuid = inv_cat->getParentUUID();
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id);
const BOOL move_is_from_outbox = model->isObjectDescendentOf(cat_id, outbox_id);
+ const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
+ const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id);
// check to make sure source is agent inventory, and is represented there.
LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
@@ -2435,6 +2623,12 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
}
}
}
+ if (is_movable && move_is_into_marketplacelistings)
+ {
+ // One cannot move a folder into a stock folder
+ is_movable = (getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK);
+ // *TODO : Merov : Add case if (nesting depth source + depth destination) > marketplace limit -> FALSE
+ }
if (is_movable)
{
@@ -2537,6 +2731,10 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
{
copy_folder_to_outbox(inv_cat, mUUID, cat_id, LLToolDragAndDrop::getOperationId());
}
+ else if (move_is_into_marketplacelistings)
+ {
+ move_folder_to_marketplacelistings(inv_cat, mUUID);
+ }
else
{
if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false)))
@@ -2552,11 +2750,17 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
mUUID,
move_is_into_trash);
}
+ if (move_is_from_marketplacelistings)
+ {
+ update_marketplace_category(from_folder_uuid);
+ // Clear the folder from the marketplace in case it was a listing folder (moot if not listed)
+ LLMarketplaceData::instance().deleteListing(cat_id);
+ }
}
}
else if (LLToolDragAndDrop::SOURCE_WORLD == source)
{
- if (move_is_into_outbox)
+ if (move_is_into_outbox || move_is_into_marketplacelistings)
{
tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
accept = FALSE;
@@ -2568,7 +2772,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
}
else if (LLToolDragAndDrop::SOURCE_LIBRARY == source)
{
- if (move_is_into_outbox)
+ if (move_is_into_outbox || move_is_into_marketplacelistings)
{
tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
accept = FALSE;
@@ -2942,7 +3146,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
else if ("cut" == action)
{
cutToClipboard();
- gInventory.removeObject(mUUID);
+ removeObject(model, mUUID);
return;
}
else if ("copy" == action)
@@ -2970,6 +3174,61 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
restoreItem();
return;
}
+ else if ("marketplace_list" == action)
+ {
+ if (depth_nesting_in_marketplace(mUUID) == 1)
+ {
+ LLMarketplaceData::instance().setActivation(mUUID,true);
+ }
+ return;
+ }
+ else if ("marketplace_activate" == action)
+ {
+ if (depth_nesting_in_marketplace(mUUID) == 2)
+ {
+ LLInventoryCategory* category = gInventory.getCategory(mUUID);
+ LLMarketplaceData::instance().setVersionFolderID(category->getParentUUID(), mUUID);
+ }
+ return;
+ }
+ else if ("marketplace_unlist" == action)
+ {
+ if (depth_nesting_in_marketplace(mUUID) == 1)
+ {
+ LLMarketplaceData::instance().setActivation(mUUID,false);
+ }
+ return;
+ }
+ else if ("marketplace_deactivate" == action)
+ {
+ if (depth_nesting_in_marketplace(mUUID) == 2)
+ {
+ LLInventoryCategory* category = gInventory.getCategory(mUUID);
+ LLMarketplaceData::instance().setVersionFolderID(category->getParentUUID(), LLUUID::null);
+ }
+ return;
+ }
+ else if ("marketplace_create_listing" == action)
+ {
+ LLMarketplaceData::instance().addListing(mUUID);
+ return;
+ }
+ else if ("marketplace_disassociate_listing" == action)
+ {
+ LLMarketplaceData::instance().deleteListing(mUUID);
+ return;
+ }
+ else if ("marketplace_associate_listing" == action)
+ {
+ LLFloaterAssociateListing::show(mUUID);
+ return;
+ }
+ else if ("marketplace_edit_listing" == action)
+ {
+ std::string url = LLMarketplaceData::instance().getListingURL(mUUID);
+ LLUrlAction::openURL(url);
+ return;
+ }
#ifndef LL_RELEASE_FOR_DOWNLOAD
else if ("delete_system_folder" == action)
{
@@ -3068,10 +3327,15 @@ LLUIImagePtr LLFolderBridge::getIcon() const
{
LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
LLViewerInventoryCategory* cat = getCategory();
- if(cat)
+ if (cat)
{
preferred_type = cat->getPreferredType();
}
+ if ((preferred_type == LLFolderType::FT_NONE) && (depth_nesting_in_marketplace(mUUID) == 2))
+ {
+ // We override the type when in the marketplace listings folder and only for version folder
+ preferred_type = LLFolderType::FT_MARKETPLACE_VERSION;
+ }
return getIcon(preferred_type);
}
@@ -3083,7 +3347,13 @@ LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type)
LLUIImagePtr LLFolderBridge::getIconOpen() const
{
- return LLUI::getUIImage(LLViewerFolderType::lookupIconName(getPreferredType(), TRUE));
+ LLFolderType::EType preferred_type = getPreferredType();
+ if ((preferred_type == LLFolderType::FT_NONE) && (depth_nesting_in_marketplace(mUUID) == 2))
+ {
+ // We override the type when in the marketplace listings folder and only for version folder
+ preferred_type = LLFolderType::FT_MARKETPLACE_VERSION;
+ }
+ return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, TRUE));
}
@@ -3180,10 +3450,12 @@ void LLFolderBridge::pasteFromClipboard()
{
const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id);
+ const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
LLDynamicArray<LLUUID> objects;
LLClipboard::instance().pasteFromClipboard(objects);
@@ -3255,21 +3527,37 @@ void LLFolderBridge::pasteFromClipboard()
LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id);
llassert(vicat);
if (vicat)
- {
- //changeCategoryParent() implicity calls dirtyFilter
- changeCategoryParent(model, vicat, parent_id, FALSE);
+ {
+ // Clear the cut folder from the marketplace if it was a listing folder (moot if not listed)
+ LLMarketplaceData::instance().deleteListing(item_id);
+ if (move_is_into_marketplacelistings)
+ {
+ move_folder_to_marketplacelistings(vicat, parent_id);
+ }
+ else
+ {
+ //changeCategoryParent() implicity calls dirtyFilter
+ changeCategoryParent(model, vicat, parent_id, FALSE);
+ }
}
}
else
- {
- LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
- llassert(viitem);
- if (viitem)
- {
- //changeItemParent() implicity calls dirtyFilter
- changeItemParent(model, viitem, parent_id, FALSE);
- }
- }
+ {
+ LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
+ llassert(viitem);
+ if (viitem)
+ {
+ if (move_is_into_marketplacelistings)
+ {
+ move_item_to_marketplacelistings(viitem, parent_id);
+ }
+ else
+ {
+ //changeItemParent() implicity calls dirtyFilter
+ changeItemParent(model, viitem, parent_id, FALSE);
+ }
+ }
+ }
}
else
{
@@ -3280,22 +3568,41 @@ void LLFolderBridge::pasteFromClipboard()
llassert(vicat);
if (vicat)
{
- copy_inventory_category(model, vicat, parent_id);
+ if (move_is_into_marketplacelistings)
+ {
+ move_folder_to_marketplacelistings(vicat, parent_id, true);
+ }
+ else
+ {
+ copy_inventory_category(model, vicat, parent_id);
+ }
}
}
- else
- {
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- parent_id,
- std::string(),
- LLPointer<LLInventoryCallback>(NULL));
- }
- }
- }
- }
+ else
+ {
+ LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
+ llassert(viitem);
+ if (viitem)
+ {
+ if (move_is_into_marketplacelistings)
+ {
+ move_item_to_marketplacelistings(viitem, parent_id, true);
+ }
+ else
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ parent_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ }
+ }
+ }
// Change mode to paste for next paste
LLClipboard::instance().setCutMode(false);
}
@@ -3308,12 +3615,14 @@ void LLFolderBridge::pasteLinkFromClipboard()
{
const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id);
+ const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
- if (move_is_into_outbox)
+ if (move_is_into_outbox || move_is_into_marketplacelistings)
{
// Notify user of failure somehow -- play error sound? modal dialog?
return;
@@ -3412,6 +3721,10 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
{
disabled_items.push_back(std::string("New Folder"));
}
+ if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ }
if(trash_id == mUUID)
{
// This is the trash.
@@ -3441,12 +3754,14 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
{
items.push_back(std::string("New Folder"));
}
-
- items.push_back(std::string("New Script"));
- items.push_back(std::string("New Note"));
- items.push_back(std::string("New Gesture"));
- items.push_back(std::string("New Clothes"));
- items.push_back(std::string("New Body Parts"));
+ if (!isMarketplaceListingsFolder())
+ {
+ items.push_back(std::string("New Script"));
+ items.push_back(std::string("New Note"));
+ items.push_back(std::string("New Gesture"));
+ items.push_back(std::string("New Clothes"));
+ items.push_back(std::string("New Body Parts"));
+ }
}
#if SUPPORT_ENSEMBLES
// Changing folder types is an unfinished unsupported feature
@@ -3490,9 +3805,9 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
LLIsType is_object( LLAssetType::AT_OBJECT );
LLIsType is_gesture( LLAssetType::AT_GESTURE );
- if (checkFolderForContentsOfType(model, is_wearable) ||
- checkFolderForContentsOfType(model, is_object) ||
- checkFolderForContentsOfType(model, is_gesture) )
+ if (checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
{
mWearables=TRUE;
}
@@ -3504,7 +3819,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
disabled_items.push_back(std::string("Delete System Folder"));
}
- if (!isOutboxFolder())
+ if (!isOutboxFolder() && !isMarketplaceListingsFolder())
{
items.push_back(std::string("Share"));
if (!canShare())
@@ -3533,8 +3848,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
{
// it's all on its way - add an observer, and the inventory will call done for us when everything is here.
gInventory.addObserver(fetch);
- }
-}
+ }
+ }
}
void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items)
@@ -3551,6 +3866,12 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
if (isItemInTrash()) return;
if (!isAgentInventory()) return;
if (isOutboxFolder()) return;
+
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+ if (isMarketplaceListingsFolder()) return;
LLFolderType::EType type = category->getPreferredType();
const bool is_system_folder = LLFolderType::lookupIsProtectedType(type);
@@ -3570,11 +3891,6 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
}
}
- if (!isItemRemovable())
- {
- disabled_items.push_back(std::string("Delete"));
- }
-
#ifndef LL_RELEASE_FOR_DOWNLOAD
if (LLFolderType::lookupIsProtectedType(type))
{
@@ -3983,6 +4299,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false);
const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false);
const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID from_folder_uuid = inv_item->getParentUUID();
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
const BOOL move_is_into_favorites = (mUUID == favorites_id);
@@ -3990,6 +4308,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id);
const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id);
const BOOL move_is_from_outbox = model->isObjectDescendentOf(inv_item->getUUID(), outbox_id);
+ const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
+ const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id);
LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
BOOL accept = FALSE;
@@ -4086,6 +4406,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
}
}
}
+ else if (move_is_into_marketplacelistings)
+ {
+ accept = (getCategory() && getCategory()->acceptItem(inv_item));
+ }
LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
@@ -4126,7 +4450,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
LLFolderViewItem* itemp = destination_panel->getRootFolder()->getDraggingOverItem();
if (itemp)
{
- LLUUID srcItemId = inv_item->getUUID();
+ LLUUID srcItemId = inv_item->getUUID();
LLUUID destItemId = static_cast<LLFolderViewModelItemInventory*>(itemp->getViewModelItem())->getUUID();
LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId);
}
@@ -4144,6 +4468,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
{
dropToOutfit(inv_item, move_is_into_current_outfit);
}
+ // MERCHANT OUTBOX folder
+ // Move the item
else if (move_is_into_outbox)
{
if (move_is_from_outbox)
@@ -4155,6 +4481,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
copy_item_to_outbox(inv_item, mUUID, LLUUID::null, LLToolDragAndDrop::getOperationId());
}
}
+ // MARKETPLACE LISTINGS folder
+ // Move the item
+ else if (move_is_into_marketplacelistings)
+ {
+ move_item_to_marketplacelistings(inv_item, mUUID);
+ }
// NORMAL or TRASH folder
// (move the item, restamp if into trash)
else
@@ -4171,8 +4503,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
mUUID,
move_is_into_trash);
}
+
+ if (move_is_from_marketplacelistings)
+ {
+ update_marketplace_category(from_folder_uuid);
+ }
- //
+ //
//--------------------------------------------------------------------------------
}
}
@@ -4221,7 +4558,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
{
accept = FALSE;
}
- else if (move_is_into_outbox)
+ else if (move_is_into_outbox || move_is_into_marketplacelistings)
{
tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
accept = FALSE;
@@ -4259,7 +4596,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
}
else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
{
- if (move_is_into_outbox)
+ if (move_is_into_outbox || move_is_into_marketplacelistings)
{
tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
accept = FALSE;
@@ -4293,7 +4630,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
{
accept = TRUE;
- if (move_is_into_outbox)
+ if (move_is_into_outbox || move_is_into_marketplacelistings)
{
tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
accept = FALSE;
@@ -4466,6 +4803,12 @@ void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
addOutboxContextMenuOptions(flags, items, disabled_items);
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
items.push_back(std::string("Share"));
@@ -4533,6 +4876,12 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
addOutboxContextMenuOptions(flags, items, disabled_items);
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
if (isItemInTrash())
@@ -4591,6 +4940,12 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
addOutboxContextMenuOptions(flags, items, disabled_items);
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
if(isItemInTrash())
@@ -4841,6 +5196,12 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Delete"));
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
items.push_back(std::string("Share"));
@@ -5108,6 +5469,12 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Delete"));
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
items.push_back(std::string("Share"));
@@ -5162,6 +5529,12 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Delete"));
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
if(isItemInTrash())
@@ -5441,6 +5814,12 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Delete"));
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
items.push_back(std::string("Share"));
@@ -5663,6 +6042,12 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Delete"));
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{ // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere
BOOL can_open = ((flags & SUPPRESS_OPEN_ITEM) != SUPPRESS_OPEN_ITEM);
@@ -5963,6 +6348,12 @@ void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
addOutboxContextMenuOptions(flags, items, disabled_items);
}
+ else if (isMarketplaceListingsFolder())
+ {
+ addMarketplaceContextMenuOptions(flags, items, disabled_items);
+ items.push_back(std::string("Properties"));
+ getClipboardEntries(false, items, disabled_items, flags);
+ }
else
{
items.push_back(std::string("Properties"));
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index bc875e8f37..72b92b6911 100755
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -146,6 +146,9 @@ protected:
virtual void addOutboxContextMenuOptions(U32 flags,
menuentry_vec_t &items,
menuentry_vec_t &disabled_items);
+ virtual void addMarketplaceContextMenuOptions(U32 flags,
+ menuentry_vec_t &items,
+ menuentry_vec_t &disabled_items);
protected:
LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid);
@@ -160,6 +163,7 @@ protected:
BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox
BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox
BOOL isOutboxFolderDirectParent() const;
+ BOOL isMarketplaceListingsFolder() const; // true if descendant of Marketplace listings folder
const LLUUID getOutboxFolder() const;
virtual BOOL isItemPermissive() const;
@@ -183,6 +187,7 @@ protected:
mutable std::string mSearchableName;
void purgeItem(LLInventoryModel *model, const LLUUID &uuid);
+ void removeObject(LLInventoryModel *model, const LLUUID &uuid);
virtual void buildDisplayName() const {}
};
@@ -274,7 +279,8 @@ public:
virtual LLUIImagePtr getIcon() const;
virtual LLUIImagePtr getIconOpen() const;
virtual LLUIImagePtr getIconOverlay() const;
-
+ virtual std::string getLabelSuffix() const;
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
static LLUIImagePtr getIcon(LLFolderType::EType preferred_type);
virtual BOOL renameItem(const std::string& new_name);
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index 15463e0d33..cf5e87c717 100755
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -33,6 +33,8 @@
#include "llfolderviewitem.h"
#include "llinventorymodel.h"
#include "llinventorymodelbackgroundfetch.h"
+#include "llinventoryfunctions.h"
+#include "llmarketplacefunctions.h"
#include "llviewercontrol.h"
#include "llfolderview.h"
#include "llinventorybridge.h"
@@ -131,6 +133,35 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
LLInventoryModelBackgroundFetch::instance().start(folder_id);
}
+ // Marketplace folder filtering
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ if (depth > 0)
+ {
+ const U32 filterTypes = mFilterOps.mFilterTypes;
+ LLUUID listing_uuid = nested_parent_id(folder_id, depth);
+ if (filterTypes & FILTERTYPE_MARKETPLACE_ACTIVE)
+ {
+ if (!LLMarketplaceData::instance().getActivationState(listing_uuid))
+ {
+ return false;
+ }
+ }
+ else if (filterTypes & FILTERTYPE_MARKETPLACE_INACTIVE)
+ {
+ if (!LLMarketplaceData::instance().isListed(listing_uuid) || LLMarketplaceData::instance().getActivationState(listing_uuid))
+ {
+ return false;
+ }
+ }
+ else if (filterTypes & FILTERTYPE_MARKETPLACE_UNASSOCIATED)
+ {
+ if (LLMarketplaceData::instance().isListed(listing_uuid))
+ {
+ return false;
+ }
+ }
+ }
+
// Always check against the clipboard
const BOOL passed_clipboard = checkAgainstClipboard(folder_id);
@@ -250,7 +281,7 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInvent
}
}
}
-
+
return TRUE;
}
@@ -475,6 +506,21 @@ void LLInventoryFilter::setFilterEmptySystemFolders()
mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS;
}
+void LLInventoryFilter::setFilterMarketplaceActiveFolders()
+{
+ mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_ACTIVE;
+}
+
+void LLInventoryFilter::setFilterMarketplaceInactiveFolders()
+{
+ mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_INACTIVE;
+}
+
+void LLInventoryFilter::setFilterMarketplaceUnassociatedFolders()
+{
+ mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_UNASSOCIATED;
+}
+
void LLInventoryFilter::setFilterUUID(const LLUUID& object_id)
{
if (mFilterOps.mFilterUUID == LLUUID::null)
diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h
index ce516af0b9..113596b0eb 100755
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -52,7 +52,10 @@ public:
FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it
FILTERTYPE_DATE = 0x1 << 3, // search by date range
FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type
- FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if
+ FILTERTYPE_EMPTYFOLDERS = 0x1 << 5, // pass if folder is not a system folder to be hidden if empty
+ FILTERTYPE_MARKETPLACE_ACTIVE = 0x1 << 6, // pass if folder is a marketplace active folder
+ FILTERTYPE_MARKETPLACE_INACTIVE = 0x1 << 7, // pass if folder is a marketplace inactive folder
+ FILTERTYPE_MARKETPLACE_UNASSOCIATED = 0x1 << 8 // pass if folder is a marketplace non associated (no market ID) folder
};
enum EFilterLink
@@ -67,7 +70,8 @@ public:
SO_NAME = 0, // Sort inventory by name
SO_DATE = 0x1, // Sort inventory by date
SO_FOLDERS_BY_NAME = 0x1 << 1, // Force folder sort by name
- SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top
+ SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2,// Force system folders to be on top
+ SO_FOLDERS_BY_WEIGHT = 0x1 << 3, // Force folder sort by weight, usually, amount of some elements in their descendents
};
struct FilterOps
@@ -160,6 +164,9 @@ public:
void setFilterUUID(const LLUUID &object_id);
void setFilterWearableTypes(U64 types);
void setFilterEmptySystemFolders();
+ void setFilterMarketplaceActiveFolders();
+ void setFilterMarketplaceInactiveFolders();
+ void setFilterMarketplaceUnassociatedFolders();
void updateFilterTypes(U64 types, U64& current_types);
void setFilterSubString(const std::string& string);
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index f1a4889f5a..10a5ac4bc7 100755
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -59,6 +59,7 @@
#include "llinventorypanel.h"
#include "lllineeditor.h"
#include "llmarketplacenotifications.h"
+#include "llmarketplacefunctions.h"
#include "llmenugl.h"
#include "llnotificationsutil.h"
#include "llpanelmaininventory.h"
@@ -90,6 +91,14 @@
BOOL LLInventoryState::sWearNewClothing = FALSE;
LLUUID LLInventoryState::sWearNewClothingTransactionID;
+// Helper function : callback to update a folder after inventory action happened in the background
+void update_folder_cb(const LLUUID& dest_folder)
+{
+ LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder);
+ gInventory.updateCategory(dest_cat);
+ gInventory.notifyObservers();
+}
+
// Generates a string containing the path to the item specified by
// item_id.
void append_path(const LLUUID& id, std::string& path)
@@ -111,6 +120,80 @@ void append_path(const LLUUID& id, std::string& path)
path.append(temp);
}
+void update_marketplace_folder_hierarchy(const LLUUID cat_id)
+{
+ // When changing the marketplace status of a folder, the only thing that needs to happen is
+ // for all observers of the folder to, possibly, change the display label of the folder
+ // so that's the only thing we change on the update mask.
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id);
+ gInventory.notifyObservers();
+
+ // Update all descendent folders down
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array);
+
+ LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
+ {
+ LLInventoryCategory* category = *iter;
+ update_marketplace_folder_hierarchy(category->getUUID());
+ }
+ return;
+}
+
+void update_marketplace_category(const LLUUID& cat_id)
+{
+ // When changing the marketplace status of a folder, we usually have to change the status of all
+ // folders in the same listing. This is because the display of each folder is affected by the
+ // overall status of the whole listing.
+ // Consequently, the only way to correctly update a folder anywhere in the marketplace is to
+ // update the whole listing from its listing root.
+ // This is not as bad as it seems as we only update folders, not items, and the folder nesting depth
+ // is limited to 4.
+ // We also take care of degenerated cases so we don't update all folders in the inventory by mistake.
+
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ // No marketplace -> likely called too early... or
+ // Not a descendent of the marketplace listings root -> likely called in error then...
+ if (marketplace_listings_uuid.isNull() || !gInventory.isObjectDescendentOf(cat_id, marketplace_listings_uuid))
+ {
+ // In those cases, just do the regular category update
+ LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
+ gInventory.updateCategory(cat);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ // Grab marketplace listing data for this folder
+ S32 depth = depth_nesting_in_marketplace(cat_id);
+ LLUUID listing_uuid = nested_parent_id(cat_id, depth);
+
+ // Verify marketplace data consistency for this listing
+ if (LLMarketplaceData::instance().isListed(listing_uuid))
+ {
+ LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolderID(listing_uuid);
+ if (version_folder_uuid.notNull() && !gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid))
+ {
+ // *TODO : Confirm with Producer that this is what we want to happen in that case!
+ llinfos << "Merov : Unlist as the version folder is not under the listing folder anymore!!" << llendl;
+ LLMarketplaceData::instance().setVersionFolderID(listing_uuid, LLUUID::null);
+ LLMarketplaceData::instance().setActivation(listing_uuid, false);
+ }
+ if (!gInventory.isObjectDescendentOf(listing_uuid, marketplace_listings_uuid))
+ {
+ // *TODO : Confirm with Producer that this is what we want to happen in that case!
+ llinfos << "Merov : Disassociate as the listing folder is not under the marketplace folder anymore!!" << llendl;
+ LLMarketplaceData::instance().deleteListing(listing_uuid);
+ }
+ }
+
+ // Update all descendents starting from the listing root
+ update_marketplace_folder_hierarchy(listing_uuid);
+
+ return;
+}
+
void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)
{
LLViewerInventoryCategory* cat;
@@ -153,13 +236,14 @@ void copy_inventory_category(LLInventoryModel* model,
for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
{
LLInventoryItem* item = *iter;
+ LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid));
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
new_cat_uuid,
std::string(),
- LLPointer<LLInventoryCallback>(NULL));
+ cb);
}
// Copy all the folders
@@ -511,14 +595,17 @@ void open_outbox()
LLFloaterReg::showInstance("outbox");
}
-LLUUID create_folder_in_outbox_for_item(LLInventoryItem* item, const LLUUID& destFolderId, S32 operation_id)
+// Create a new folder in destFolderId with the same name as the item name and return the uuid of the new folder
+// Note: this is used locally in various situation where we need to wrap an item into a special folder
+LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId)
{
llassert(item);
llassert(destFolderId.notNull());
LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName());
gInventory.notifyObservers();
-
+
+ // *TODO : Create different notifications for the various cases
LLNotificationsUtil::add("OutboxFolderCreated");
return created_folder_id;
@@ -534,8 +621,7 @@ void move_to_outbox_cb_action(const LLSD& payload)
// when moving item directly into outbox create folder with that name
if (dest_folder_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false))
{
- S32 operation_id = payload["operation_id"].asInteger();
- dest_folder_id = create_folder_in_outbox_for_item(viitem, dest_folder_id, operation_id);
+ dest_folder_id = create_folder_for_item(viitem, dest_folder_id);
}
LLUUID parent = viitem->getParentUUID();
@@ -606,7 +692,7 @@ void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LL
// when moving item directly into outbox create folder with that name
if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false))
{
- dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id);
+ dest_folder = create_folder_for_item(inv_item, dest_folder);
}
copy_inventory_item(gAgent.getID(),
@@ -636,7 +722,7 @@ void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32
// when moving item directly into outbox create folder with that name
if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false))
{
- dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id);
+ dest_folder = create_folder_for_item(inv_item, dest_folder);
}
LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item;
@@ -677,6 +763,479 @@ void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_fold
}
///----------------------------------------------------------------------------
+// Marketplace functions
+//
+// Handles Copy and Move to or within the Marketplace listings folder.
+// Handles creation of stock folders, nesting of listings and version folders,
+// permission checking and listings validation.
+///----------------------------------------------------------------------------
+
+S32 depth_nesting_in_marketplace(LLUUID cur_uuid)
+{
+ // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ if (marketplace_listings_uuid.isNull())
+ {
+ return -1;
+ }
+ // If not a descendent of the marketplace listings root, then the nesting depth is -1 by definition
+ if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid))
+ {
+ return -1;
+ }
+
+ // Iterate through the parents till we hit the marketplace listings root
+ // Note that the marketplace listings root itself will return 0
+ S32 depth = 0;
+ LLInventoryObject* cur_object = gInventory.getObject(cur_uuid);
+ while (cur_uuid != marketplace_listings_uuid)
+ {
+ depth++;
+ cur_uuid = cur_object->getParentUUID();
+ cur_object = gInventory.getCategory(cur_uuid);
+ }
+ return depth;
+}
+
+// Returns the UUID of the marketplace listing this object is in
+LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth)
+{
+ LLInventoryObject* cur_object = gInventory.getObject(cur_uuid);
+ cur_uuid = (depth < 1 ? LLUUID::null : cur_uuid);
+ while (depth > 1)
+ {
+ depth--;
+ cur_uuid = cur_object->getParentUUID();
+ cur_object = gInventory.getCategory(cur_uuid);
+ }
+ return cur_uuid;
+}
+
+S32 compute_stock_count(LLUUID cat_uuid)
+{
+ // Handle the case of the folder being a stock folder immediately
+ LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid);
+ if (!cat)
+ {
+ // Not a category so no stock count to speak of
+ return -1;
+ }
+ if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
+ {
+ // Note: stock folders are *not* supposed to have nested subfolders so we stop recursion here
+ // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated
+ return cat->getDescendentCount();
+ }
+
+ // Grab marketplace data for this folder
+ S32 depth = depth_nesting_in_marketplace(cat_uuid);
+ LLUUID listing_uuid = nested_parent_id(cat_uuid, depth);
+ if (!LLMarketplaceData::instance().isListed(listing_uuid))
+ {
+ // If not listed, the notion of stock is meaningless so it won't be computed for any level
+ return -1;
+ }
+
+ LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolderID(listing_uuid);
+ // Handle the case of the first 2 levels : listing and version folders
+ if (depth == 1)
+ {
+ if (version_folder_uuid.notNull())
+ {
+ // If there is a version folder, the stock value for the listing is the version folder stock
+ return compute_stock_count(version_folder_uuid);
+ }
+ else
+ {
+ // If there's no version folder associated, the notion of stock count has no meaning
+ return -1;
+ }
+ }
+ else if (depth == 2)
+ {
+ if (version_folder_uuid.notNull() && (version_folder_uuid != cat_uuid))
+ {
+ // If there is a version folder but we're not it, our stock count is meaningless
+ return -1;
+ }
+ }
+
+ // In all other cases, the stock count is the min of stock folders count found in the descendents
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array);
+
+ // "-1" denotes a folder that doesn't countain any stock folders in its descendents
+ S32 curr_count = -1;
+
+ // Note: marketplace listings have a maximum depth nesting of 4
+ LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
+ {
+ LLInventoryCategory* category = *iter;
+ S32 count = compute_stock_count(category->getUUID());
+ if ((curr_count == -1) || ((count != -1) && (count < curr_count)))
+ {
+ curr_count = count;
+ }
+ }
+
+ return curr_count;
+}
+
+void move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy)
+{
+ // Get the marketplace listings, exit with error if none
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ if (marketplace_listings_uuid.isNull())
+ {
+ llinfos << "Merov : Marketplace error : There is no marketplace listings folder -> move aborted!" << llendl;
+ return;
+ }
+
+ // We will collapse links into items/folders
+ LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item;
+ LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory();
+
+ if (linked_category != NULL)
+ {
+ // Move the linked folder directly
+ move_folder_to_marketplacelistings(linked_category, dest_folder, copy);
+ }
+ else
+ {
+ // Grab the linked item if any
+ LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem();
+ viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item);
+
+ // Check that the agent has transfer permission on the item: this is required as a resident cannot
+ // put on sale items she cannot transfer. Proceed with move if we have permission.
+ if (viewer_inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()))
+ {
+ // When moving an isolated item directly under the marketplace listings root, we create a new folder with that name
+ if (dest_folder == marketplace_listings_uuid)
+ {
+ dest_folder = create_folder_for_item(inv_item, dest_folder);
+ }
+ LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder);
+
+ // Verify we can have this item in that destination category
+ if (!dest_cat->acceptItem(viewer_inv_item))
+ {
+ llinfos << "Merov : Marketplace error : Cannot move item in that stock folder -> move aborted!" << llendl;
+ return;
+ }
+
+ // When moving a no copy item into a first level listing folder, we create a stock folder for it
+ if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && (dest_cat->getParentUUID() == marketplace_listings_uuid))
+ {
+ dest_folder = create_folder_for_item(inv_item, dest_folder);
+ }
+
+ // Get the parent folder of the moved item : we may have to update it
+ LLUUID src_folder = viewer_inv_item->getParentUUID();
+
+ if (copy)
+ {
+ // Copy the item
+ LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder));
+ copy_inventory_item(
+ gAgent.getID(),
+ viewer_inv_item->getPermissions().getOwner(),
+ viewer_inv_item->getUUID(),
+ dest_folder,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ // Reparent the item
+ gInventory.changeItemParent(viewer_inv_item, dest_folder, false);
+ }
+
+ // Validate the destination : note that this will run the validation code only on one listing folder at most...
+ validate_marketplacelistings(dest_cat);
+
+ // Update the modified folders
+ update_marketplace_category(src_folder);
+ update_marketplace_category(dest_folder);
+ }
+ else
+ {
+ // *TODO : signal an error to the user (UI for this TBD)
+ llinfos << "Merov : Marketplace error : User doesn't have the correct permission to put this item on sale -> move aborted!" << llendl;
+ }
+ }
+}
+
+void move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy)
+{
+ // Check that we have adequate permission on all items being moved. Proceed if we do.
+ if (has_correct_permissions_for_sale(inv_cat))
+ {
+ // Get the destination folder
+ LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder);
+
+ // Check it's not a stock folder
+ if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
+ {
+ llinfos << "Merov : Marketplace error : Cannot move folder in stock folder -> move aborted!" << llendl;
+ return;
+ }
+
+ // Get the parent folder of the moved item : we may have to update it
+ LLUUID src_folder = inv_cat->getParentUUID();
+
+ LLViewerInventoryCategory * viewer_inv_cat = (LLViewerInventoryCategory *) inv_cat;
+ if (copy)
+ {
+ // Copy the folder
+ copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder);
+ }
+ else
+ {
+ // Reparent the folder
+ gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false);
+ }
+
+ // Check the destination folder recursively for no copy items and promote the including folders if any
+ validate_marketplacelistings(dest_cat);
+
+ // Update the modified folders
+ update_marketplace_category(src_folder);
+ update_marketplace_category(dest_folder);
+ }
+}
+
+// Returns true if all items within the argument folder are fit for sale, false otherwise
+bool has_correct_permissions_for_sale(LLInventoryCategory* cat)
+{
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array);
+
+ LLInventoryModel::item_array_t item_array_copy = *item_array;
+
+ for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
+ {
+ LLInventoryItem* item = *iter;
+ LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item;
+ LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory();
+ LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem();
+ // Linked items and folders cannot be put for sale
+ if (linked_category || linked_item)
+ {
+ llinfos << "Merov : linked items in this folder -> not allowed to sell!" << llendl;
+ return false;
+ }
+ // Check that the agent has transfer permission on the item: this is required as a resident cannot
+ // put on sale items she cannot transfer. Proceed with move if we have permission.
+ if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()))
+ {
+ llinfos << "Merov : wrong permissions on items in this folder -> not allowed to sell!" << llendl;
+ return false;
+ }
+ }
+
+ LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+
+ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
+ {
+ LLInventoryCategory* category = *iter;
+ if (!has_correct_permissions_for_sale(category))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Make all relevant business logic checks on the marketplace listings starting with the folder as argument
+// This function does no deletion of listings but a mere audit and raises issues to the user
+// The only thing that's done is to move and sort folders containing no-copy items to stock folders
+// *TODO : Add the rest of the SLM/AIS business logic (limit of nesting depth, stock folder consistency, overall limit on listings, etc...)
+void validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb)
+{
+ // Special case a stock folder depth issue
+ LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat);
+ const LLFolderType::EType folder_type = cat->getPreferredType();
+ S32 depth = depth_nesting_in_marketplace(cat->getUUID());
+ if (depth == 1)
+ {
+ std::string message = "Validating listing : " + cat->getName();
+ llinfos << "Merov : Validation log : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ }
+ if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth <= 2))
+ {
+ // Nest the stock folder one level deeper in a normal folder and restart from there
+ //LLUUID parent_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ LLUUID parent_uuid = cat->getParentUUID();
+ LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName());
+ std::string message = " Warning : creating wrapping folder for stock folder : " + cat->getName();
+ llinfos << "Merov : Validation warning : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid);
+ gInventory.changeCategoryParent(viewer_cat, folder_uuid, false);
+ validate_marketplacelistings(new_cat, cb);
+ return;
+ }
+
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array);
+
+ // Stock items : sorting and moving the various stock items is complicated as the set of constraints is high
+ // For each folder, we need to:
+ // * separate non stock items, stock items per types in different folders
+ // * have stock items nested at depth 2 at least
+ // * never ever move the non-stock items
+
+ std::vector<std::vector<LLViewerInventoryItem*> > items_vector;
+ items_vector.resize(LLInventoryType::IT_COUNT+1);
+
+ // Parse the items and create vectors of items to sort copyable items and stock items of various types
+ LLInventoryModel::item_array_t item_array_copy = *item_array;
+ for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
+ {
+ LLInventoryItem* item = *iter;
+ LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item;
+ LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory();
+ LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem();
+ // Skip items that shouldn't be there to start with, raise an error message for those
+ if (linked_category || linked_item)
+ {
+ std::string message = " Error : linked item are not allowed in listings : " + viewer_inv_item->getName();
+ llinfos << "Merov : Validation error : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ continue;
+ }
+ if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()))
+ {
+ std::string message = " Error : item with incorrect permissions in listing : " + viewer_inv_item->getName();
+ llinfos << "Merov : Validation error : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ continue;
+ }
+ // Update the appropriate vector item for that type
+ LLInventoryType::EType type = LLInventoryType::IT_COUNT; // Default value for non stock items
+ if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()))
+ {
+ // Get the item type for stock items
+ type = viewer_inv_item->getInventoryType();
+ }
+ items_vector[type].push_back(viewer_inv_item);
+ }
+ // How many types of folders? Which type is it if only one?
+ S32 count = 0;
+ LLInventoryType::EType type = LLInventoryType::IT_COUNT;
+ for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++)
+ {
+ if (!items_vector[i].empty())
+ {
+ count++;
+ type = (LLInventoryType::EType)(i);
+ }
+ }
+ // If we have no items in there (only folders) -> all OK
+ if (count == 0)
+ {
+ std::string message = " Log : folder validates: doesn't contain any item";
+ llinfos << "Merov : Validation log : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ }
+ // If we have one kind only, in the correct folder type at the right depth -> all OK
+ else if ((count == 1) && (((type == LLInventoryType::IT_COUNT) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2))))
+ {
+ // Done with that folder!
+ std::string message = " Log : folder validates: all items of compatible types";
+ llinfos << "Merov : Validation log : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ }
+ else
+ {
+ // Create one folder per vector at the right depth and of the right type
+ for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++)
+ {
+ if (!items_vector[i].empty())
+ {
+ // Create a new folder
+ std::string message = " Warning : creating stock folder : " + viewer_cat->getName();
+ llinfos << "Merov : Validation warning : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID());
+ LLFolderType::EType new_folder_type = (i == LLInventoryType::IT_COUNT ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK);
+ LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, viewer_cat->getName());
+ // Move each item to the new folder
+ while (!items_vector[i].empty())
+ {
+ LLViewerInventoryItem* viewer_inv_item = items_vector[i].back();
+ std::string message = " Warning : moving item : " + viewer_inv_item->getName();
+ llinfos << "Merov : Validation warning : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ gInventory.changeItemParent(viewer_inv_item, folder_uuid, false);
+ items_vector[i].pop_back();
+ }
+ update_marketplace_category(folder_uuid);
+ }
+ }
+ // Clean up
+ if (viewer_cat->getDescendentCount() == 0)
+ {
+ // Remove the current folder if it ends up empty
+ llinfos << "Merov : Validation warning : " << llendl;
+ std::string message = " Warning : folder content completely moved to stock folder -> removing empty folder";
+ llinfos << "Merov : Validation warning : " << message << llendl;
+ if (cb)
+ {
+ cb(message);
+ }
+ gInventory.removeCategory(cat->getUUID());
+ gInventory.notifyObservers();
+ return;
+ }
+ else
+ {
+ // Update the current folder
+ update_marketplace_category(cat->getUUID());
+ }
+ }
+
+ // Recursion : Perform the same validation on each nested folder
+ LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+
+ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
+ {
+ LLInventoryCategory* category = *iter;
+ validate_marketplacelistings(category, cb);
+ }
+}
+
+///----------------------------------------------------------------------------
/// LLInventoryCollectFunctor implementations
///----------------------------------------------------------------------------
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index f1066a4dc9..a31bc9dbdd 100755
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -58,6 +58,9 @@ void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id);
void show_item_original(const LLUUID& item_uuid);
void reset_inventory_filter();
+// Just nudge the category in the global inventory to signal that its marketplace status changed
+void update_marketplace_category(const LLUUID& cat_id);
+
void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null);
@@ -67,9 +70,18 @@ void append_path(const LLUUID& id, std::string& path);
void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id);
void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id);
-
void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id);
+typedef boost::function<void(std::string& validation_message)> validation_callback_t;
+
+void move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false);
+void move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false);
+bool has_correct_permissions_for_sale(LLInventoryCategory* cat);
+void validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL);
+S32 depth_nesting_in_marketplace(LLUUID cur_uuid);
+LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth);
+S32 compute_stock_count(LLUUID cat_uuid);
+
/** Miscellaneous global functions
** **
*******************************************************************************/
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index ed7fd3cd34..c0aedd3881 100755
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1013,7 +1013,7 @@ LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLU
// an existing item with the matching id, or it will add the category.
void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
{
- if(cat->getUUID().isNull())
+ if(!cat || cat->getUUID().isNull())
{
return;
}
@@ -1027,7 +1027,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
if(old_cat)
{
- // We already have an old category, modify it's values
+ // We already have an old category, modify its values
U32 mask = LLInventoryObserver::NONE;
LLUUID old_parent_id = old_cat->getParentUUID();
LLUUID new_parent_id = cat->getParentUUID();
@@ -1052,7 +1052,13 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
{
mask |= LLInventoryObserver::LABEL;
}
- old_cat->copyViewerCategory(cat);
+ // Under marketplace, category labels are quite complex and need extra upate
+ const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id))
+ {
+ mask |= LLInventoryObserver::LABEL;
+ }
+ old_cat->copyViewerCategory(cat);
addChangedMask(mask, cat->getUUID());
}
else
@@ -2160,12 +2166,12 @@ void LLInventoryModel::buildParentChildMap()
// implement it, we would need a set or map of uuid pairs
// which would be (folder_id, new_parent_id) to be sent up
// to the server.
- llinfos << "Lost categroy: " << cat->getUUID() << " - "
+ llinfos << "Lost category: " << cat->getUUID() << " - "
<< cat->getName() << llendl;
++lost;
// plop it into the lost & found.
LLFolderType::EType pref = cat->getPreferredType();
- if(LLFolderType::FT_NONE == pref)
+ if ((LLFolderType::FT_NONE == pref) || (LLFolderType::FT_MARKETPLACE_STOCK == pref))
{
cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
}
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 81ee7ac07e..b0163b5996 100755
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -288,6 +288,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX));
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX));
+ getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS));
}
// set the filter for the empty folder if the debug setting is on
@@ -567,7 +568,8 @@ void LLInventoryPanel::modelChanged(U32 mask)
else if (!model_item && view_item && viewmodel_item)
{
// Remove the item's UI.
- removeItemID(viewmodel_item->getUUID());
+ const LLUUID& idp = viewmodel_item->getUUID();
+ removeItemID(idp);
view_item->destroyView();
}
}
@@ -1489,5 +1491,8 @@ namespace LLInitParam
declare(LLFolderType::lookup(LLFolderType::FT_INBOX) , LLFolderType::FT_INBOX);
declare(LLFolderType::lookup(LLFolderType::FT_OUTBOX) , LLFolderType::FT_OUTBOX);
declare(LLFolderType::lookup(LLFolderType::FT_BASIC_ROOT) , LLFolderType::FT_BASIC_ROOT);
+ declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_LISTINGS) , LLFolderType::FT_MARKETPLACE_LISTINGS);
+ declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_STOCK), LLFolderType::FT_MARKETPLACE_STOCK);
+ declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_VERSION), LLFolderType::FT_MARKETPLACE_VERSION);
}
}
diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp
index 05c9d76810..40417bf77b 100755
--- a/indra/newview/llmarketplacefunctions.cpp
+++ b/indra/newview/llmarketplacefunctions.cpp
@@ -30,12 +30,14 @@
#include "llagent.h"
#include "llhttpclient.h"
+#include "llinventoryfunctions.h"
#include "llsdserialize.h"
#include "lltimer.h"
#include "lltrans.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
#include "llviewernetwork.h"
+#include "llviewerregion.h"
//
@@ -425,6 +427,18 @@ void LLMarketplaceInventoryImporter::initialize()
return;
}
+ // Test DirectDelivery cap
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string url = region->getCapability("DirectDelivery");
+ llinfos << "Merov : Test DirectDelivery cap : url = " << url << llendl;
+ }
+ else
+ {
+ llinfos << "Merov : Test DirectDelivery cap : no region accessible" << llendl;
+ }
+
if (!LLMarketplaceImport::hasSessionCookie())
{
mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
@@ -528,3 +542,240 @@ void LLMarketplaceInventoryImporter::updateImport()
}
}
+//
+// Direct Delivery : Marketplace tuples and data
+//
+
+// Tuple == Item
+LLMarketplaceTuple::LLMarketplaceTuple() :
+ mListingFolderId(),
+ mListingId(0),
+ mVersionFolderId(),
+ mIsActive(false)
+{
+}
+
+LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id) :
+ mListingFolderId(folder_id),
+ mListingId(0),
+ mVersionFolderId(),
+ mIsActive(false)
+{
+}
+
+LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed) :
+ mListingFolderId(folder_id),
+ mListingId(listing_id),
+ mVersionFolderId(version_id),
+ mIsActive(is_listed)
+{
+}
+
+
+// Data map
+LLMarketplaceData::LLMarketplaceData()
+{
+ mTestCurrentMarketplaceID = 1234567;
+}
+
+// Creation / Deletion
+bool LLMarketplaceData::addListing(const LLUUID& folder_id)
+{
+ if (isListed(folder_id))
+ {
+ // Listing already exists -> exit with error
+ return false;
+ }
+ mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id);
+
+ // *TODO : Create the listing on SLM and get the ID (blocking?)
+ // For the moment, we use that wonky test ID generator...
+ S32 listing_id = LLMarketplaceData::instance().getTestMarketplaceID();
+
+ setListingID(folder_id,listing_id);
+ update_marketplace_category(folder_id);
+ return true;
+}
+
+bool LLMarketplaceData::associateListing(const LLUUID& folder_id, S32 listing_id)
+{
+ if (isListed(folder_id))
+ {
+ // Listing already exists -> exit with error
+ return false;
+ }
+ mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id);
+
+ // Check that the listing ID is not already associated to some other record
+ LLUUID old_listing = getListingFolder(listing_id);
+ if (old_listing.notNull())
+ {
+ // If it is already used, unlist the old record (we can't have 2 listings with the same listing ID)
+ deleteListing(old_listing);
+ }
+
+ setListingID(folder_id,listing_id);
+ update_marketplace_category(folder_id);
+ return true;
+}
+
+bool LLMarketplaceData::deleteListing(const LLUUID& folder_id)
+{
+ if (!isListed(folder_id))
+ {
+ // Listing doesn't exist -> exit with error
+ return false;
+ }
+ mMarketplaceItems.erase(folder_id);
+ update_marketplace_category(folder_id);
+ return true;
+}
+
+// Accessors
+bool LLMarketplaceData::getActivationState(const LLUUID& folder_id)
+{
+ // Listing folder case
+ if (isListed(folder_id))
+ {
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it->second).mIsActive;
+ }
+ // We need to iterate through the list to check it's not a version folder
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mVersionFolderId == folder_id)
+ {
+ return (it->second).mIsActive;
+ }
+ it++;
+ }
+ return false;
+}
+
+S32 LLMarketplaceData::getListingID(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it == mMarketplaceItems.end() ? 0 : (it->second).mListingId);
+}
+
+LLUUID LLMarketplaceData::getVersionFolderID(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it == mMarketplaceItems.end() ? LLUUID::null : (it->second).mVersionFolderId);
+}
+
+// Reverse lookup : find the listing folder id from the listing id
+LLUUID LLMarketplaceData::getListingFolder(S32 listing_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mListingId == listing_id)
+ {
+ return (it->second).mListingFolderId;
+ }
+ it++;
+ }
+ return LLUUID::null;
+}
+
+bool LLMarketplaceData::isListed(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it != mMarketplaceItems.end());
+}
+
+bool LLMarketplaceData::isVersionFolder(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mVersionFolderId == folder_id)
+ {
+ return true;
+ }
+ it++;
+ }
+ return false;
+}
+
+std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id)
+{
+ // Get the listing id (i.e. go up the hierarchy to find the listing folder
+ // URL format will be something like : https://marketplace.secondlife.com/p/listing/<listing_id>
+ std::string marketplace_url = getMarketplaceURL("MarketplaceURL");
+
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ LLUUID listing_uuid = nested_parent_id(folder_id, depth);
+ S32 listing_id = getListingID(listing_uuid);
+
+ if (listing_id != 0)
+ {
+ marketplace_url += llformat("p/listing/%d",listing_id);
+ }
+ return marketplace_url;
+}
+
+// Modifiers
+bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ if (it == mMarketplaceItems.end())
+ {
+ return false;
+ }
+ else
+ {
+ (it->second).mListingId = listing_id;
+ update_marketplace_category(folder_id);
+ return true;
+ }
+}
+
+bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ if (it == mMarketplaceItems.end())
+ {
+ return false;
+ }
+ else
+ {
+ LLUUID old_version_id = (it->second).mVersionFolderId;
+ if (old_version_id != version_id)
+ {
+ (it->second).mVersionFolderId = version_id;
+ update_marketplace_category(old_version_id);
+ update_marketplace_category(version_id);
+ }
+ return true;
+ }
+}
+
+bool LLMarketplaceData::setActivation(const LLUUID& folder_id, bool activate)
+{
+ // Listing folder case
+ if (isListed(folder_id))
+ {
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ (it->second).mIsActive = activate;
+ update_marketplace_category((it->second).mListingFolderId);
+ return true;
+ }
+ // We need to iterate through the list to check it's not a version folder
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mVersionFolderId == folder_id)
+ {
+ (it->second).mIsActive = activate;
+ update_marketplace_category((it->second).mListingFolderId);
+ return true;
+ }
+ it++;
+ }
+ return false;
+}
+
+
diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h
index abe60890a3..a587419323 100755
--- a/indra/newview/llmarketplacefunctions.h
+++ b/indra/newview/llmarketplacefunctions.h
@@ -109,6 +109,74 @@ private:
};
+// Classes handling the data coming from and going to the Marketplace DB:
+// * implement the Marketplace API (TBD)
+// * cache the current Marketplace data (tuples)
+// * provide methods to get Marketplace data on any inventory item
+// * set Marketplace data
+// * signal Marketplace updates to inventory
+class LLMarketplaceData;
+
+// A Marketplace item is known by its tuple
+class LLMarketplaceTuple
+{
+public:
+ friend class LLMarketplaceData;
+
+ LLMarketplaceTuple();
+ LLMarketplaceTuple(const LLUUID& folder_id);
+ LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed = false);
+
+private:
+ // Representation of a marketplace item in the Marketplace DB (well, what we know of it...)
+ LLUUID mListingFolderId;
+ S32 mListingId;
+ LLUUID mVersionFolderId;
+ bool mIsActive;
+};
+// Note: The listing folder UUID is used as a key to this map. It could therefore be taken off the LLMarketplaceTuple objects themselves
+typedef std::map<LLUUID, LLMarketplaceTuple> marketplace_items_list_t;
+
+// Session cache of Marketplace tuples
+// Note: There's one and only one possible set of Marketplace dataset per agent and per session
+class LLMarketplaceData
+ : public LLSingleton<LLMarketplaceData>
+{
+public:
+ LLMarketplaceData();
+
+ bool isEmpty() { return (mMarketplaceItems.size() == 0); }
+
+ // Probe the Marketplace data set to identify folders
+ bool isListed(const LLUUID& folder_id); // returns true if folder_id is a Listing folder
+ bool isVersionFolder(const LLUUID& folder_id); // returns true if folder_id is a Version folder
+
+ // Create/Delete Marketplace data set : each method returns true if the function succeeds, false if error
+ bool addListing(const LLUUID& folder_id);
+ bool associateListing(const LLUUID& folder_id, S32 listing_id);
+ bool deleteListing(const LLUUID& folder_id);
+
+ // Access Marketplace data set : each method returns a default value if the folder_id can't be found
+ bool getActivationState(const LLUUID& folder_id);
+ S32 getListingID(const LLUUID& folder_id);
+ LLUUID getVersionFolderID(const LLUUID& folder_id);
+ std::string getListingURL(const LLUUID& folder_id);
+ LLUUID getListingFolder(S32 listing_id);
+
+ // Modify Marketplace data set : each method returns true if the function succeeds, false if error
+ bool setListingID(const LLUUID& folder_id, S32 listing_id);
+ bool setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id);
+ bool setActivation(const LLUUID& folder_id, bool activate);
+
+ // Merov : Test method while waiting for SLM API
+ S32 getTestMarketplaceID() { return mTestCurrentMarketplaceID++; }
+
+private:
+ marketplace_items_list_t mMarketplaceItems;
+ // Merov : This is for test only, waiting for SLM API
+ S32 mTestCurrentMarketplaceID;
+};
+
#endif // LL_LLMARKETPLACEFUNCTIONS_H
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index a8eeddb798..3df0467e45 100755
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -79,6 +79,7 @@
#include "llfloaterland.h"
#include "llfloaterlandholdings.h"
#include "llfloatermap.h"
+#include "llfloatermarketplacelistings.h"
#include "llfloatermemleak.h"
#include "llfloaternamedesc.h"
#include "llfloaternotificationsconsole.h"
@@ -177,6 +178,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("fast_timers", "floater_fast_timers.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFastTimerView>);
LLFloaterReg::add("about_land", "floater_about_land.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLand>);
LLFloaterReg::add("appearance", "floater_my_appearance.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
+ LLFloaterReg::add("associate_listing", "floater_associate_listing.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAssociateListing>);
LLFloaterReg::add("auction", "floater_auction.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAuction>);
LLFloaterReg::add("avatar", "floater_avatar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatar>);
LLFloaterReg::add("avatar_picker", "floater_avatar_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarPicker>);
@@ -225,6 +227,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIncomingCallDialog>);
LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("inspect", "floater_inspect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInspect>);
+ LLFloaterReg::add("item_properties", "floater_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
LLInspectAvatarUtil::registerFloater();
LLInspectGroupUtil::registerFloater();
LLInspectObjectUtil::registerFloater();
@@ -243,6 +246,8 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("tex_fetch_debugger", "floater_texture_fetch_debugger.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTextureFetchDebugger>);
}
LLFloaterReg::add("media_settings", "floater_media_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMediaSettings>);
+ LLFloaterReg::add("marketplace_listings", "floater_marketplace_listings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMarketplaceListings>);
+ LLFloaterReg::add("marketplace_validation", "floater_marketplace_validation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMarketplaceValidation>);
LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
LLFloaterReg::add("message_tos", "floater_tos.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMove>);
diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp
index 991f6b40e6..1e9d0791c0 100644
--- a/indra/newview/llviewerfoldertype.cpp
+++ b/indra/newview/llviewerfoldertype.cpp
@@ -140,7 +140,11 @@ LLViewerFolderDictionary::LLViewerFolderDictionary()
addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "Inv_SysOpen", "Inv_SysClosed", FALSE, boxes_invisible));
addEntry(LLFolderType::FT_BASIC_ROOT, new ViewerFolderEntry("Basic Root", "Inv_SysOpen", "Inv_SysClosed", FALSE, true));
-
+
+ addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new ViewerFolderEntry("Marketplace listings", "Inv_SysOpen", "Inv_SysClosed", FALSE, boxes_invisible));
+ addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new ViewerFolderEntry("New Stock", "Inv_StockFolderOpen", "Inv_StockFolderClosed", FALSE, false, "default"));
+ addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new ViewerFolderEntry("New Version", "Inv_VersionFolderOpen","Inv_VersionFolderClosed", FALSE, false, "default"));
+
addEntry(LLFolderType::FT_NONE, new ViewerFolderEntry("New Folder", "Inv_FolderOpen", "Inv_FolderClosed", FALSE, false, "default"));
#if SUPPORT_ENSEMBLES
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index fff9821e86..4ca569e78c 100755
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -816,6 +816,29 @@ bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const
return true;
}
+bool LLViewerInventoryCategory::acceptItem(LLInventoryItem* inv_item)
+{
+ bool accept = true;
+ // Only stock folders have limitation on which item they will accept
+ if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
+ {
+ // If the item is copyable (i.e. non stock) do not accept the drop in a stock folder
+ if (inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()))
+ {
+ accept = false;
+ }
+ else
+ {
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(getUUID(),cat_array,item_array);
+ // Destination stock folder must be empty OR types of incoming and existing items must be identical
+ accept = (!item_array->count() || (item_array->get(0)->getInventoryType() == inv_item->getInventoryType()));
+ }
+ }
+ return accept;
+}
+
void LLViewerInventoryCategory::determineFolderType()
{
/* Do NOT uncomment this code. This is for future 2.1 support of ensembles.
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index ab19a12014..6b33ef0672 100755
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -225,6 +225,9 @@ public:
bool importFileLocal(LLFILE* fp);
void determineFolderType();
void changeType(LLFolderType::EType new_folder_type);
+
+ // returns true if the category object will accept the incoming item
+ bool acceptItem(LLInventoryItem* inv_item);
private:
friend class LLInventoryModel;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index a271690349..7a57badf79 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1593,6 +1593,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("CopyInventoryFromNotecard");
capabilityNames.append("CreateInventoryCategory");
capabilityNames.append("DispatchRegionInfo");
+ capabilityNames.append("DirectDelivery");
capabilityNames.append("EnvironmentSettings");
capabilityNames.append("EstateChangeInfo");
capabilityNames.append("EventQueueGet");
diff --git a/indra/newview/skins/default/textures/icons/Inv_StockFolderClosed.png b/indra/newview/skins/default/textures/icons/Inv_StockFolderClosed.png
new file mode 100644
index 0000000000..4dc484dc22
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Inv_StockFolderClosed.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Inv_StockFolderOpen.png b/indra/newview/skins/default/textures/icons/Inv_StockFolderOpen.png
new file mode 100644
index 0000000000..0d140b56a7
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Inv_StockFolderOpen.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Inv_VersionFolderClosed.png b/indra/newview/skins/default/textures/icons/Inv_VersionFolderClosed.png
new file mode 100644
index 0000000000..e89a4d7f31
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Inv_VersionFolderClosed.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Inv_VersionFolderOpen.png b/indra/newview/skins/default/textures/icons/Inv_VersionFolderOpen.png
new file mode 100644
index 0000000000..659d7d392f
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Inv_VersionFolderOpen.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 94c187e21a..9c81271308 100755
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -300,6 +300,8 @@ with the same filename but different name
<texture name="Inv_Snapshot" file_name="icons/Inv_Snapshot.png" preload="false" />
<texture name="Inv_Socks" file_name="icons/Inv_Socks.png" preload="false" />
<texture name="Inv_Sound" file_name="icons/Inv_Sound.png" preload="false" />
+ <texture name="Inv_StockFolderClosed" file_name="icons/Inv_StockFolderClosed.png" preload="false" />
+ <texture name="Inv_StockFolderOpen" file_name="icons/Inv_StockFolderOpen.png" preload="false" />
<texture name="Inv_SysClosed" file_name="icons/Inv_SysClosed.png" preload="false" />
<texture name="Inv_SysOpen" file_name="icons/Inv_SysOpen.png" preload="false" />
<texture name="Inv_Tattoo" file_name="icons/Inv_Tattoo.png" preload="false" />
@@ -311,6 +313,8 @@ with the same filename but different name
<texture name="Inv_Undershirt" file_name="icons/Inv_Undershirt.png" preload="false" />
<texture name="Inv_Link" file_name="icons/Inv_Link.png" preload="false" />
<texture name="Inv_Invalid" file_name="icons/Inv_Invalid.png" preload="false" />
+ <texture name="Inv_VersionFolderClosed" file_name="icons/Inv_VersionFolderClosed.png" preload="false" />
+ <texture name="Inv_VersionFolderOpen" file_name="icons/Inv_VersionFolderOpen.png" preload="false" />
<texture name="Linden_Dollar_Alert" file_name="widgets/Linden_Dollar_Alert.png"/>
<texture name="Linden_Dollar_Background" file_name="widgets/Linden_Dollar_Background.png"/>
diff --git a/indra/newview/skins/default/xui/en/floater_associate_listing.xml b/indra/newview/skins/default/xui/en/floater_associate_listing.xml
new file mode 100755
index 0000000000..e019ed58dd
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_associate_listing.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_minimize="false"
+ height="110"
+ layout="topleft"
+ name="associate listing"
+ help_topic="associate_listing"
+ title="ASSOCIATE LISTING"
+ width="375">
+ <text
+ type="string"
+ length="1"
+ follows="top|left"
+ font="SansSerifLarge"
+ height="16"
+ layout="topleft"
+ left="10"
+ top="25"
+ name="message">
+ Listing ID:
+ </text>
+ <line_editor
+ type="string"
+ length="1"
+ follows="top|right"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ left_delta="0"
+ name="listing_id"
+ top_pad="5"
+ width="350">
+ Type ID here
+ </line_editor>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="OK"
+ layout="topleft"
+ left="155"
+ name="OK"
+ top_pad="10"
+ width="100" />
+ <button
+ follows="bottom|right"
+ height="23"
+ label="Cancel"
+ layout="topleft"
+ left_pad="5"
+ name="Cancel"
+ width="100" />
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_item_properties.xml b/indra/newview/skins/default/xui/en/floater_item_properties.xml
new file mode 100755
index 0000000000..0fc54a9c8b
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_item_properties.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ height="570"
+ layout="topleft"
+ name="Item Properties"
+ help_topic="item+properties"
+ title="ITEM PROPERTIES"
+ width="330">
+ <panel
+ follows="all"
+ layout="topleft"
+ left="0"
+ class="sidepanel_item_info"
+ filename="sidepanel_item_info.xml"
+ name="item_panel"
+ top="20"
+ label=""
+ height="570"
+ visible="true"
+ width="330">
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_marketplace_listings.xml b/indra/newview/skins/default/xui/en/floater_marketplace_listings.xml
new file mode 100755
index 0000000000..f5630aeecb
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_marketplace_listings.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<floater
+ title="MARKETPLACE LISTINGS"
+ name="floater_marketplace_listings"
+ help_topic="floater_marketplace_listings"
+ positioning="cascading"
+ width="333"
+ height="445"
+ min_width="200"
+ min_height="300"
+ can_close="true"
+ can_resize="true"
+ save_rect="true"
+ save_visibility="false"
+ reuse_instance="true">
+ <string name="MarketplaceListingsInitializing">Initializing...</string>
+ <panel
+ name="marketplace_listings_panel"
+ follows="all"
+ layout="topleft"
+ left="0"
+ top="0"
+ height="440"
+ width="333">
+ <panel
+ follows="all"
+ left="10"
+ height="420"
+ width="313"
+ top="0"
+ bg_opaque_color="InventoryBackgroundColor">
+ <panel
+ name="marketplace_listings_inventory_placeholder_panel"
+ follows="all"
+ layout="topleft"
+ top="0"
+ left="0"
+ width="313"
+ height="420"
+ bg_opaque_color="InventoryBackgroundColor">
+ <text
+ name="marketplace_listings_inventory_placeholder_title"
+ type="string"
+ follows="top|left|right"
+ layout="topleft"
+ top="10"
+ left="0"
+ width="313"
+ height="25"
+ wrap="true"
+ halign="center"
+ font="SansSerifBold">
+ Loading...
+ </text>
+ <text
+ name="marketplace_listings_inventory_placeholder_text"
+ type="string"
+ follows="top|left|right"
+ layout="topleft"
+ top="35"
+ left="0"
+ width="313"
+ height="130"
+ wrap="true"
+ halign="left" />
+ </panel>
+ <panel
+ name="panel_marketplace_listing"
+ filename="panel_marketplace_listings.xml"
+ class="llpanelmarketplacelistings"
+ top="0"
+ follows="all"/>
+ </panel>
+ <panel
+ name="marketplace_panel_status"
+ follows="bottom|left|right"
+ layout="topleft"
+ left="10"
+ width="313"
+ height="20">
+ <text
+ name="marketplace_status"
+ type="string"
+ follows="bottom|left|right"
+ layout="topleft"
+ top="0"
+ left="5"
+ width="150"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ font="SansSerif"/>
+ </panel>
+ <layout_stack name="initialization_progress_indicator" orientation="vertical" left="0" height="440" top="0" width="333" follows="all" visible="false">
+ <layout_panel />
+ <layout_panel height="24" auto_resize="false">
+ <layout_stack orientation="horizontal" left="0" height="24" top="0" width="333" follows="all">
+ <layout_panel width="0" />
+ <layout_panel width="24" auto_resize="false">
+ <loading_indicator
+ height="24"
+ layout="topleft"
+ left="0"
+ top="0"
+ width="24" />
+ </layout_panel>
+ <layout_panel width="0" />
+ </layout_stack>
+ </layout_panel>
+ <layout_panel />
+ </layout_stack>
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_marketplace_validation.xml b/indra/newview/skins/default/xui/en/floater_marketplace_validation.xml
new file mode 100755
index 0000000000..165540cfa1
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_marketplace_validation.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ title="MARKETPLACE VALIDATION LOG"
+ name="floater_marketplace_validation"
+ help_topic="floater_marketplace_validation"
+ layout="topleft"
+ positioning="cascading"
+ legacy_header_height="18"
+ width="600"
+ height="500"
+ min_width="400"
+ min_height="200"
+ can_close="true"
+ can_resize="true"
+ can_minimize="true"
+ save_rect="true"
+ reuse_instance="true"
+ save_visibility="false">
+ <button
+ name="OK"
+ label="OK"
+ label_selected="OK"
+ layout="topleft"
+ follows="right|bottom"
+ top="465"
+ width="100"
+ height="20"
+ left="484"/>
+ <text
+ name="tos_heading"
+ type="string"
+ font="SansSerif"
+ length="1"
+ layout="topleft"
+ follows="left|top"
+ width="552"
+ height="20"
+ left="20"
+ top="20">
+ The Marketplace Validation reported the following:
+ </text>
+ <text_editor
+ name="validation_text"
+ type="string"
+ font="SansSerif"
+ length="1"
+ max_length="65536"
+ layout="topleft"
+ follows="all"
+ bg_readonly_color="TextBgReadOnlyColor"
+ text_readonly_color="TextFgReadOnlyColor"
+ top="45"
+ bottom="455"
+ left="20"
+ right="-20"
+ word_wrap="true">
+ MARKETPLACE_VALIDATION_TEXT
+ </text_editor>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index 6fa45d7d66..b621d53a6c 100755
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -4,6 +4,73 @@
layout="topleft"
name="Popup"
visible="false">
+ <menu_item_call
+ label="Create Listing"
+ layout="topleft"
+ name="Marketplace Create Listing">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_create_listing" />
+ </menu_item_call>
+ <menu_item_call
+ label="Associate Listing"
+ layout="topleft"
+ name="Marketplace Associate Listing">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_associate_listing" />
+ </menu_item_call>
+ <menu_item_call
+ label="Disassociate Listing"
+ layout="topleft"
+ name="Marketplace Disassociate Listing">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_disassociate_listing" />
+ </menu_item_call>
+ <menu_item_call
+ label="Edit Listing"
+ layout="topleft"
+ name="Marketplace Edit Listing">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_edit_listing" />
+ </menu_item_call>
+ <menu_item_call
+ label="List"
+ layout="topleft"
+ name="Marketplace List">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_list" />
+ </menu_item_call>
+ <menu_item_call
+ label="Unlist"
+ layout="topleft"
+ name="Marketplace Unlist">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_unlist" />
+ </menu_item_call>
+ <menu_item_call
+ label="Activate"
+ layout="topleft"
+ name="Marketplace Activate">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_activate" />
+ </menu_item_call>
+ <menu_item_call
+ label="Deactivate"
+ layout="topleft"
+ name="Marketplace Deactivate">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="marketplace_deactivate" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Marketplace Listings Separator" />
<menu_item_call
label="Share"
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/menu_marketplace_view.xml b/indra/newview/skins/default/xui/en/menu_marketplace_view.xml
new file mode 100755
index 0000000000..cff5ec1040
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_marketplace_view.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ name="menu_marketplace_sort"
+ left="0" bottom="0" visible="false"
+ mouse_opaque="false">
+ <menu_item_check
+ label="Sort by stock amount (low to high)"
+ name="sort_by_stock_amount">
+ <menu_item_check.on_click
+ function="Marketplace.ViewSort.Action"
+ parameter="sort_by_stock_amount"/>
+ <menu_item_check.on_check
+ function="Marketplace.ViewSort.CheckItem"
+ parameter="sort_by_stock_amount"/>
+ </menu_item_check>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 7e8d2aaf9a..2c432c2c8c 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -181,6 +181,13 @@
function="Floater.ToggleOrBringToFront"
parameter="outbox" />
</menu_item_call>
+ <menu_item_call
+ label="Marketplace listings..."
+ name="MarketplaceListings">
+ <menu_item_call.on_click
+ function="Floater.ToggleOrBringToFront"
+ parameter="marketplace_listings" />
+ </menu_item_call>
<menu_item_call
label="Account dashboard..."
name="Manage My Account">
diff --git a/indra/newview/skins/default/xui/en/panel_marketplace_listings.xml b/indra/newview/skins/default/xui/en/panel_marketplace_listings.xml
new file mode 100755
index 0000000000..0ebb7a5f48
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_marketplace_listings.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ label="Marketplace"
+ name="Marketplace Panel"
+ follows="all"
+ layout="topleft"
+ width="308"
+ height="440">
+ <panel
+ name="tool_panel"
+ follows="left|top|right"
+ layout="topleft"
+ height="30"
+ width="308"
+ top="0"
+ left="0">
+ <menu_button
+ name="sort_btn"
+ tool_tip="View/sort options"
+ layout="topleft"
+ follows="top|left"
+ width="31"
+ height="25"
+ left="2"
+ menu_filename="menu_marketplace_view.xml"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Conv_toolbar_sort"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ menu_position="bottomleft"/>
+ <button
+ name="add_btn"
+ tool_tip="Create a new listing folder"
+ layout="topleft"
+ follows="top|left"
+ width="31"
+ height="25"
+ left_pad="2"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Conv_toolbar_plus"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"/>
+ <button
+ name="audit_btn"
+ tool_tip="Validate your marketplace listings"
+ layout="topleft"
+ follows="top|left"
+ width="31"
+ height="25"
+ left_pad="2"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Info"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"/>
+ </panel>
+ <panel
+ name="tab_container_panel"
+ follows="all"
+ layout="topleft"
+ default_tab_group="1"
+ width="308"
+ height="400">
+ <tab_container
+ name="marketplace_filter_tabs"
+ follows="all"
+ layout="topleft"
+ top="0"
+ left="0"
+ top_pad="0"
+ width="308"
+ height="400"
+ halign="center"
+ tab_height="30"
+ tab_group="1"
+ tab_position="top"
+ tab_min_width="50">
+ <inventory_panel
+ label="ALL"
+ name="All Items"
+ help_topic="marketplace_tab"
+ layout="topleft"
+ follows="all"
+ width="308"
+ height="370"
+ top="16"
+ left="0"
+ start_folder.name="Marketplace listings"
+ show_empty_message="false"
+ show_load_status="false"
+ start_folder.type="merchant"
+ tool_tip="Drag and drop items here to sell them"
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ show_item_link_overlays="true">
+ </inventory_panel>
+ <inventory_panel
+ label="LISTED"
+ name="Active Items"
+ help_topic="marketplace_tab"
+ layout="topleft"
+ follows="all"
+ width="308"
+ height="370"
+ left_delta="0"
+ start_folder.name="Marketplace listings"
+ show_empty_message="false"
+ show_load_status="false"
+ start_folder.type="merchant"
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ show_item_link_overlays="true">
+ </inventory_panel>
+ <inventory_panel
+ label="UNLISTED"
+ name="Inactive Items"
+ help_topic="marketplace_tab"
+ layout="topleft"
+ follows="all"
+ width="308"
+ height="370"
+ left_delta="0"
+ start_folder.name="Marketplace listings"
+ show_empty_message="false"
+ show_load_status="false"
+ start_folder.type="merchant"
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ show_item_link_overlays="true">
+ </inventory_panel>
+ <inventory_panel
+ label="UNASSOCIATED"
+ name="Unassociated Items"
+ help_topic="marketplace_tab"
+ layout="topleft"
+ follows="all"
+ width="308"
+ height="370"
+ left_delta="0"
+ start_folder.name="Marketplace listings"
+ show_empty_message="false"
+ show_load_status="false"
+ start_folder.type="merchant"
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ show_item_link_overlays="true">
+ </inventory_panel>
+ </tab_container>
+ </panel>
+</panel> \ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 3252ed2b62..ab7df75216 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -2255,6 +2255,11 @@ We are accessing your account on the [[MARKETPLACE_CREATE_STORE_URL] Marketplace
<string name="InventoryOutboxError">
The [[MARKETPLACE_CREATE_STORE_URL] Marketplace store] is returning errors.
</string>
+ <string name="InventoryMarketplaceListingsNoItemsTitle">Your Marketplace Listings folder is empty.</string>
+ <string name="InventoryMarketplaceListingsNoItemsTooltip"></string>
+ <string name="InventoryMarketplaceListingsNoItems">
+ Drag folders to this area to list them for sale on the [[MARKETPLACE_DASHBOARD_URL] Marketplace].
+ </string>
<string name="Marketplace Error None">No errors</string>
<string name="Marketplace Error Not Merchant">Error: Before sending items to the Marketplace you will need to set yourself up as a merchant (free of charge).</string>
@@ -2266,6 +2271,13 @@ The [[MARKETPLACE_CREATE_STORE_URL] Marketplace store] is returning errors.
<string name="Marketplace Error Unsellable Item">Error: This item can not be sold on the marketplace.</string>
<string name="Marketplace Error Internal Import">Error: There was a problem with this item. Try again later.</string>
+ <string name="MarketplaceNoID">no Mkt ID</string>
+ <string name="MarketplaceLive">listed</string>
+ <string name="MarketplaceActive">active</string>
+ <string name="MarketplaceMax">max</string>
+ <string name="MarketplaceStock">stock</string>
+ <string name="MarketplaceNoStock">out of stock</string>
+
<string name="Open landmarks">Open landmarks</string>
<!-- use value="" because they have preceding spaces -->
@@ -3911,6 +3923,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Inventory_Label">Inventory</string>
<string name="Command_Map_Label">Map</string>
<string name="Command_Marketplace_Label">Marketplace</string>
+ <string name="Command_MarketplaceListings_Label">Sell on Marketplace</string>
<string name="Command_MiniMap_Label">Mini-map</string>
<string name="Command_Move_Label">Walk / run / fly</string>
<string name="Command_Outbox_Label">Merchant outbox</string>
@@ -3939,6 +3952,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Inventory_Tooltip">View and use your belongings</string>
<string name="Command_Map_Tooltip">Map of the world</string>
<string name="Command_Marketplace_Tooltip">Go shopping</string>
+ <string name="Command_MarketplaceListings_Tooltip">Sell your creation</string>
<string name="Command_MiniMap_Tooltip">Show nearby people</string>
<string name="Command_Move_Tooltip">Moving your avatar</string>
<string name="Command_Outbox_Tooltip">Transfer items to your marketplace for sale</string>