From f140b4f35c36d0b1de0c2f98542e6d3ca687455d Mon Sep 17 00:00:00 2001 From: Inusaito Sayori Date: Thu, 30 Jul 2015 11:45:19 -0400 Subject: [PATCH] Giant VMM merge, still need to compile it and fix llfloatermarketplacelistings Merge VMM up to change 187902d9c4afd2990acddeb964c928179a52d189 mAllowWear=>mAllowOpen (should probably just remove this altogether though inventory_panels now accept the "show_root_folder", "allow_drop_on_root", "allow_open", and "use_marketplace_folders" attributes. Other stuff in this commit: LLPanelInjector~ Sync lltooldraganddrop to alchemy Sync notifications up with alchemy Reorganize notifications some, to better merge with upstream Remove duplicate InventorySortOrder in settings map There's a bit of a weird folderview merge mixed into here, I wouldn't trust it, but this thing is so god damn tangled I can't go back now~ Wouldn't have been possible without help from Shyotl and music by Amanda Palmer --- indra/llcommon/llassettype.h | 3 + indra/llui/llpanel.cpp | 25 +- indra/llui/llpanel.h | 59 +- indra/newview/app_settings/settings.xml | 31 +- indra/newview/hippolimits.cpp | 3 +- indra/newview/llagent.cpp | 6 + indra/newview/llappviewer.cpp | 2 +- .../newview/llfloatermarketplacelistings.cpp | 365 +++- indra/newview/llfloatermarketplacelistings.h | 49 +- indra/newview/llfloateroutbox.cpp | 2 +- indra/newview/llfolderviewitem.cpp | 24 +- indra/newview/llfolderviewitem.h | 4 +- indra/newview/llinventoryactions.cpp | 59 +- indra/newview/llinventorybridge.cpp | 600 +++++-- indra/newview/llinventorybridge.h | 42 +- indra/newview/llinventoryfilter.cpp | 42 +- indra/newview/llinventoryfilter.h | 12 +- indra/newview/llinventoryfunctions.cpp | 451 +++-- indra/newview/llinventoryfunctions.h | 14 +- indra/newview/llinventorymodel.cpp | 23 +- indra/newview/llinventorypanel.cpp | 328 +++- indra/newview/llinventorypanel.h | 42 +- indra/newview/llmarketplacefunctions.cpp | 460 +++-- indra/newview/llmarketplacefunctions.h | 59 +- indra/newview/llpanelmaininventory.cpp | 56 +- indra/newview/llstartup.cpp | 6 +- indra/newview/lltooldraganddrop.cpp | 324 ++-- indra/newview/lltooldraganddrop.h | 40 +- indra/newview/llviewerfoldertype.cpp | 1 + indra/newview/llviewerinventory.cpp | 7 +- indra/newview/llviewermenu.cpp | 30 +- indra/newview/llvoavatarself.cpp | 2 +- indra/newview/llvoavatarself.h | 2 +- .../Marketplace_Dropzone_Background.png | Bin 0 -> 459 bytes .../skins/default/textures/textures.xml | 2 + .../en-us/floater_marketplace_listings.xml | 1 - .../default/xui/en-us/menu_inventory.xml | 16 +- .../xui/en-us/menu_marketplace_view.xml | 48 +- .../skins/default/xui/en-us/notifications.xml | 1580 ++++++++++++----- .../xui/en-us/panel_marketplace_listings.xml | 36 +- .../panel_marketplace_listings_inventory.xml | 3 +- .../panel_marketplace_listings_listed.xml | 3 +- ...anel_marketplace_listings_unassociated.xml | 3 +- .../panel_marketplace_listings_unlisted.xml | 3 +- .../xui/en-us/panel_outbox_inventory.xml | 2 + .../skins/default/xui/en-us/strings.xml | 18 +- 46 files changed, 3480 insertions(+), 1408 deletions(-) create mode 100644 indra/newview/skins/default/textures/Marketplace_Dropzone_Background.png diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index e6fee54ca..8de144542 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -122,6 +122,9 @@ public: AT_LINK_FOLDER = 25, // Inventory folder link + + AT_MARKETPLACE_FOLDER = 26, + // Marketplace folder. Same as an AT_CATEGORY but different display methods. AT_CURRENT_OUTFIT = 46, diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index a42853023..e311daa1c 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -441,8 +441,31 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac std::string name("panel"); node->getAttributeString("name", name); - LLPanel* panelp = factory->createFactoryPanel(name); + std::string class_attr; + node->getAttributeString("class", class_attr); + + LLPanel* panelp = NULL; + + { + if(!class_attr.empty()) + { + panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr); + } + + if (!panelp) + { + panelp = factory->createFactoryPanel(name); + /* Singu TODO: Future? + llassert(panelp); + + if (!panelp) + { + return NULL; // :( + }*/ + } + } LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION); + // Fall back on a default panel, if there was no special factory. if (!panelp) { diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index d2ca151e5..f379cd2a6 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -39,8 +39,8 @@ #include "lluictrl.h" #include "llbutton.h" #include "lllineeditor.h" -#include "lluiimage.h" #include "llviewborder.h" +#include "lluiimage.h" #include "lluistring.h" #include "v4color.h" #include @@ -142,6 +142,7 @@ public: CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; } EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } + virtual BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); void initChildrenXML(LLXMLNodePtr node, LLUICtrlFactory* factory); void setPanelParameters(LLXMLNodePtr node, LLView *parentp); @@ -151,6 +152,7 @@ public: std::string getString(const std::string& name) const; // ** Wrappers for setting child properties by name ** -TomY + // WARNING: These are deprecated, please use getChild("name")->doStuff() idiom instead // LLView void childSetVisible(const std::string& name, bool visible); @@ -196,7 +198,7 @@ public: BOOL childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text); BOOL childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text); BOOL childSetToolTipArg(const std::string& id, const std::string& key, const LLStringExplicit& text); - + // LLSlider / LLMultiSlider / LLSpinCtrl void childSetMinValue(const std::string& id, LLSD min_value); void childSetMaxValue(const std::string& id, LLSD max_value); @@ -228,6 +230,7 @@ public: static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb ); + protected: // Override to set not found list LLButton* getDefaultButton() { return mDefaultBtn; } @@ -262,4 +265,56 @@ private: }; // end class LLPanel +typedef boost::function LLPanelClassCreatorFunc; + +// local static instance for registering a particular panel class + +class LLRegisterPanelClass +: public LLSingleton< LLRegisterPanelClass > +{ +public: + // reigister with either the provided builder, or the generic templated builder + void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func) + { + mPanelClassesNames[tag] = func; + } + + LLPanel* createPanelClass(const std::string& tag) + { + param_name_map_t::iterator iT = mPanelClassesNames.find(tag); + if(iT == mPanelClassesNames.end()) + return 0; + return iT->second(); + } + template + static T* defaultPanelClassBuilder() + { + T* pT = new T(); + return pT; + } + +private: + typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t; + + param_name_map_t mPanelClassesNames; +}; + + +// local static instance for registering a particular panel class +template + class LLPanelInjector +{ +public: + // register with either the provided builder, or the generic templated builder + LLPanelInjector(const std::string& tag); +}; + + +template + LLPanelInjector::LLPanelInjector(const std::string& tag) +{ + LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder); +} + + #endif diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a704472ee..109dc8959 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1538,15 +1538,6 @@ This should be as low as possible, but too low may break functionality Value 0 - InventorySortOrder - - Comment - Specifies sort key for inventory items (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) - Type - U32 - Value - 3 - Blacklist.Settings Comment @@ -9477,6 +9468,17 @@ This should be as low as possible, but too low may break functionality Value 200 + InventoryOutboxMaxStockItemCount + + Comment + Maximum number of items allowed in a stock folder. + Persist + 0 + Type + U32 + Value + 200 + InventorySortOrder Comment @@ -9488,6 +9490,17 @@ This should be as low as possible, but too low may break functionality Value 7 + MarketplaceListingsSortOrder + + Comment + Specifies sort for marketplace listings + Persist + 1 + Type + U32 + Value + 2 + InvertMouse Comment diff --git a/indra/newview/hippolimits.cpp b/indra/newview/hippolimits.cpp index 8e7462511..2d0614497 100644 --- a/indra/newview/hippolimits.cpp +++ b/indra/newview/hippolimits.cpp @@ -53,14 +53,13 @@ void HippoLimits::setOpenSimLimits() setMaxAgentGroups(); mMaxPrimScale = 8192.0f; mMaxHeight = 10000.0f; + mMinPrimScale = 0.001f; if (gHippoGridManager->getConnectedGrid()->isRenderCompat()) { LL_INFOS() << "Using rendering compatible OpenSim limits." << LL_ENDL; - mMinPrimScale = 0.01f; mMinHoleSize = 0.05f; mMaxHollow = 0.95f; } else { LL_INFOS() << "Using Hippo OpenSim limits." << LL_ENDL; - mMinPrimScale = 0.001f; mMinHoleSize = 0.01f; mMaxHollow = 0.99f; } diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index bd2c40619..87b1e4f99 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4107,6 +4107,12 @@ void LLAgent::handleTeleportFinished() LLNotificationsUtil::add("PreferredMaturityChanged", args); mIsMaturityRatingChangingDuringTeleport = false; } + + // Init SLM Marketplace connection so we know which UI should be used for the user as a merchant + // Note: Eventually, all merchant will be migrated to the new SLM system and there will be no reason to show the old UI at all. + // Note: Some regions will not support the SLM cap for a while so we need to do that check for each teleport. + // *TODO : Suppress that line from here once the whole grid migrated to SLM and move it to idle_startup() (llstartup.cpp) + check_merchant_status(); } void LLAgent::handleTeleportFailed() diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9ae068e77..9a64b05b0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -919,7 +919,7 @@ bool LLAppViewer::init() load_default_bindings(gSavedSettings.getBOOL("LiruUseZQSDKeys")); // If we don't have the right GL requirements, exit. - if (!gGLManager.mHasRequirements && !gNoRender) + if (!gGLManager.mHasRequirements) { // can't use an alert here since we're exiting and // all hell breaks lose. diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp index 25ce9dc86..a111c84d6 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -37,8 +37,8 @@ #include "llinventoryobserver.h" #include "llinventoryfunctions.h" #include "llmarketplacefunctions.h" -#include "llnotificationhandler.h" -#include "llnotificationmanager.h" +//#include "llnotificationhandler.h" +//#include "llnotificationmanager.h" #include "llnotificationsutil.h" //#include "llsidepaneliteminfo.h" #include "lltextbox.h" @@ -48,12 +48,12 @@ /// LLPanelMarketplaceListings ///---------------------------------------------------------------------------- -static LLPanelInjector t_panel_status("llpanelmarketplacelistings"); // Liru TODO: build this instead. +static LLPanelInjector t_panel_status("llpanelmarketplacelistings"); LLPanelMarketplaceListings::LLPanelMarketplaceListings() : mRootFolder(NULL) , mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME) -, mFilterType(LLInventoryFilter::FILTERTYPE_NONE) +, mFilterListingFoldersOnly(false) { mCommitCallbackRegistrar.add("Marketplace.ViewSort.Action", boost::bind(&LLPanelMarketplaceListings::onViewSortMenuItemClicked, this, _2)); mEnableCallbackRegistrar.add("Marketplace.ViewSort.CheckItem", boost::bind(&LLPanelMarketplaceListings::onViewSortMenuItemCheck, this, _2)); @@ -73,27 +73,44 @@ BOOL LLPanelMarketplaceListings::postBuild() return LLPanel::postBuild(); } +BOOL LLPanelMarketplaceListings::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + BOOL handled = (handled_view != NULL); + // Special case the drop zone + if (handled && (handled_view->getName() == "marketplace_drop_zone")) + { + LLFolderView* root_folder = getRootFolder(); + handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + return handled; +} + void LLPanelMarketplaceListings::buildAllPanels() { // Build the All panel first LLInventoryPanel* panel_all_items; panel_all_items = buildInventoryPanel("All Items", "panel_marketplace_listings_inventory.xml"); - panel_all_items->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + //panel_all_items->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); panel_all_items->getFilter().markDefault(); // Build the other panels LLInventoryPanel* panel; panel = buildInventoryPanel("Active Items", "panel_marketplace_listings_listed.xml"); panel->getFilter().setFilterMarketplaceActiveFolders(); - panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + //panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); panel->getFilter().markDefault(); panel = buildInventoryPanel("Inactive Items", "panel_marketplace_listings_unlisted.xml"); panel->getFilter().setFilterMarketplaceInactiveFolders(); - panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + //panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); panel->getFilter().markDefault(); panel = buildInventoryPanel("Unassociated Items", "panel_marketplace_listings_unassociated.xml"); panel->getFilter().setFilterMarketplaceUnassociatedFolders(); - panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + //panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); panel->getFilter().markDefault(); // Set the tab panel @@ -101,28 +118,43 @@ void LLPanelMarketplaceListings::buildAllPanels() tabs_panel->setCommitCallback(boost::bind(&LLPanelMarketplaceListings::onTabChange, this)); tabs_panel->selectTabPanel(panel_all_items); // All panel selected by default mRootFolder = panel_all_items->getRootFolder(); // Keep the root of the all panel + + // Set the default sort order + setSortOrder(gSavedSettings.getU32("MarketplaceListingsSortOrder")); } LLInventoryPanel* LLPanelMarketplaceListings::buildInventoryPanel(const std::string& childname, const std::string& filename) { - LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + //LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + // Singu Note: These should already be built!! LLInventoryPanel* panel = getChild(childname); - if (panel) - { - tabs_panel->removeTabPanel(panel); - delete panel; - } - panel = LLUICtrlFactory::createFromFile(filename, tabs_panel, LLInventoryPanel::child_registry_t::instance()); llassert(panel != NULL); // Set sort order and callbacks panel = getChild(childname); - panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); + //panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); // Singu TODO: Do this at some point, not really important. panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2)); return panel; } +void LLPanelMarketplaceListings::setSortOrder(U32 sort_order) +{ + mSortOrder = sort_order; + gSavedSettings.setU32("MarketplaceListingsSortOrder", sort_order); + + // Set each panel with that sort order + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + LLInventoryPanel* panel = (LLInventoryPanel*)tabs_panel->getPanelByName("All Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Active Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Inactive Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Unassociated Items"); + panel->setSortOrder(mSortOrder); +} + void LLPanelMarketplaceListings::onFilterEdit(const std::string& search_string) { // Find active panel @@ -157,6 +189,12 @@ void LLPanelMarketplaceListings::onSelectionChange(LLInventoryPanel *panel, cons panel->onSelectionChange(items, user_action); } +bool LLPanelMarketplaceListings::allowDropOnRoot() +{ + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + return (panel ? panel->getAllowDropOnRoot() : false); +} + void LLPanelMarketplaceListings::onTabChange() { // Find active panel @@ -169,6 +207,19 @@ void LLPanelMarketplaceListings::onTabChange() // Set filter string on active panel panel->setFilterSubString(mFilterSubString); + + // Show/hide the drop zone and resize the inventory tabs panel accordingly + LLPanel* drop_zone = (LLPanel*)getChild("marketplace_drop_zone"); + bool drop_zone_visible = drop_zone->getVisible(); + if (drop_zone_visible != panel->getAllowDropOnRoot()) + { + LLPanel* tabs = (LLPanel*)getChild("tab_container_panel"); + S32 delta_height = drop_zone->getRect().getHeight(); + delta_height = (drop_zone_visible ? delta_height : -delta_height); + tabs->reshape(tabs->getRect().getWidth(),tabs->getRect().getHeight() + delta_height); + tabs->translate(0,-delta_height); + } + drop_zone->setVisible(panel->getAllowDropOnRoot()); } } @@ -191,7 +242,7 @@ void LLPanelMarketplaceListings::onAddButtonClicked() void LLPanelMarketplaceListings::onAuditButtonClicked() { LLSD data(LLSD::emptyMap()); - LLFloaterReg::showInstance("marketplace_validation", data); + new LLFloaterMarketplaceValidation(data); // It tracks itself *sigh* } void LLPanelMarketplaceListings::onViewSortMenuItemClicked(const LLSD& userdata) @@ -199,28 +250,62 @@ void LLPanelMarketplaceListings::onViewSortMenuItemClicked(const LLSD& userdata) std::string chosen_item = userdata.asString(); // Sort options - if (chosen_item == "sort_by_stock_amount") + if ((chosen_item == "sort_by_stock_amount") || (chosen_item == "sort_by_name") || (chosen_item == "sort_by_recent")) { - mSortOrder = (mSortOrder == LLInventoryFilter::SO_FOLDERS_BY_NAME ? LLInventoryFilter::SO_FOLDERS_BY_WEIGHT : LLInventoryFilter::SO_FOLDERS_BY_NAME); - // Set each panel with that sort order + // We're making sort options exclusive, default is SO_FOLDERS_BY_NAME + if (chosen_item == "sort_by_stock_amount") + { + setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_WEIGHT); + } + else if (chosen_item == "sort_by_name") + { + setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + else if (chosen_item == "sort_by_recent") + { + setSortOrder(LLInventoryFilter::SO_DATE); + } + } + // Filter option + else if (chosen_item == "show_only_listing_folders") + { + mFilterListingFoldersOnly = !mFilterListingFoldersOnly; + // Set each panel with that filter flag LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); LLInventoryPanel* panel = (LLInventoryPanel*)tabs_panel->getPanelByName("All Items"); - panel->setSortOrder(mSortOrder); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Active Items"); - panel->setSortOrder(mSortOrder); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Inactive Items"); - panel->setSortOrder(mSortOrder); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Unassociated Items"); - panel->setSortOrder(mSortOrder); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); } } 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; + + if ((chosen_item == "sort_by_stock_amount") || (chosen_item == "sort_by_name") || (chosen_item == "sort_by_recent")) + { + if (chosen_item == "sort_by_stock_amount") + { + return (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_WEIGHT); + } + else if (chosen_item == "sort_by_name") + { + return (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + else if (chosen_item == "sort_by_recent") + { + return (mSortOrder & LLInventoryFilter::SO_DATE); + } + } + else if (chosen_item == "show_only_listing_folders") + { + return mFilterListingFoldersOnly; + } return false; } @@ -271,7 +356,7 @@ LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key) , mInventoryText(NULL) , mInventoryTitle(NULL) , mPanelListings(NULL) -, mFirstViewListings(true) +, mPanelListingsSet(false) { //buildFromFile("floater_marketplace_listings.xml"); LLUICtrlFactory::instance().buildFloater(this, "floater_marketplace_listings.xml"); @@ -308,7 +393,7 @@ BOOL LLFloaterMarketplaceListings::postBuild() mCategoryAddedObserver = new LLMarketplaceListingsAddedObserver(this); gInventory.addObserver(mCategoryAddedObserver); - // Debug : fetch aggressively so we can create test data right onOpen() + // Fetch aggressively so we can interact with listings right onOpen() fetchContents(); return TRUE; @@ -318,18 +403,8 @@ void LLFloaterMarketplaceListings::onClose(bool app_quitting) { } -bool canAccessMarketplace() +void LLFloaterMarketplaceListings::onOpen(/*const LLSD& key*/) { - return (LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT) || gSavedSettings.getBOOL("InventoryOutboxDisplayBoth"); -} - -void LLFloaterMarketplaceListings::onOpen(const LLSD& key) -{ - if (!canAccessMarketplace()) - { - close(); - return; - } // // Initialize the Market Place or go update the marketplace listings // @@ -350,17 +425,21 @@ void LLFloaterMarketplaceListings::onFocusReceived() void LLFloaterMarketplaceListings::fetchContents() { - if (mRootFolderId.notNull()) + if (mRootFolderId.notNull() && + (LLMarketplaceData::instance().getSLMDataFetched() != MarketplaceFetchCodes::MARKET_FETCH_LOADING) && + (LLMarketplaceData::instance().getSLMDataFetched() != MarketplaceFetchCodes::MARKET_FETCH_DONE)) { + LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this)); + LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING); LLInventoryModelBackgroundFetch::instance().start(mRootFolderId); - // Get all the SLM Listings LLMarketplaceData::instance().getSLMListings(); } } -void LLFloaterMarketplaceListings::setup() +void LLFloaterMarketplaceListings::setRootFolder() { - if (LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) + if ((LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) && + (LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)) { // If we are *not* a merchant or we have no market place connection established yet, do nothing return; @@ -389,18 +468,27 @@ void LLFloaterMarketplaceListings::setup() LL_WARNS("SLM") << "Inventory warning: Marketplace listings folder already set" << LL_ENDL; return; } + mRootFolderId = marketplacelistings_id; +} + +void LLFloaterMarketplaceListings::setPanels() +{ + if (mRootFolderId.isNull()) + { + 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); + gInventory.consolidateForType(mRootFolderId, LLFolderType::FT_MARKETPLACE_LISTINGS); // Now that we do have a non NULL root, we can build the inventory panels mPanelListings->buildAllPanels(); // Create observer for marketplace listings modifications - if (!mCategoriesObserver && mRootFolderId.notNull()) + if (!mCategoriesObserver) { mCategoriesObserver = new LLInventoryCategoriesObserver(); llassert(mCategoriesObserver); @@ -410,6 +498,9 @@ void LLFloaterMarketplaceListings::setup() // Get the content of the marketplace listings folder fetchContents(); + + // Flag that this is done + mPanelListingsSet = true; } void LLFloaterMarketplaceListings::initializeMarketPlace() @@ -441,35 +532,36 @@ void LLFloaterMarketplaceListings::setStatusString(const std::string& statusStri void LLFloaterMarketplaceListings::updateView() { U32 mkt_status = LLMarketplaceData::instance().getSLMStatus(); + bool is_merchant = (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) || (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT); + U32 data_fetched = LLMarketplaceData::instance().getSLMDataFetched(); // Get or create the root folder if we are a merchant and it hasn't been done already - if (mRootFolderId.isNull() && (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT)) + if (mRootFolderId.isNull() && is_merchant) { - setup(); + setRootFolder(); } - // Update the bottom initializing status and progress dial - if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) + // Update the bottom initializing status and progress dial if we are initializing or if we're a merchant and still loading + if ((mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) || (is_merchant && (data_fetched <= MarketplaceFetchCodes::MARKET_FETCH_LOADING)) ) { - setStatusString(getString("MarketplaceListingsInitializing")); + // Just show the loading indicator in that case and fetch the data (fetch will be skipped if it's already loading) mInventoryInitializationInProgress->setVisible(true); + mPanelListings->setVisible(FALSE); + fetchContents(); + return; } else { - setStatusString(""); mInventoryInitializationInProgress->setVisible(false); } // Update the middle portion : tabs or messages if (getFolderCount() > 0) { - if (mFirstViewListings) + if (!mPanelListingsSet) { // We need to rebuild the tabs cleanly the first time we make them visible - // setup() does it if the root is nixed first - mRootFolderId.setNull(); - setup(); - mFirstViewListings = false; + setPanels(); } mPanelListings->setVisible(TRUE); mInventoryPlaceholder->setVisible(FALSE); @@ -511,7 +603,7 @@ void LLFloaterMarketplaceListings::updateView() else { // "Errors!" message strings - text = LLTrans::getString("InventoryOutboxError", subs); + text = LLTrans::getString("InventoryMarketplaceError", subs); title = LLTrans::getString("InventoryOutboxErrorTitle"); tooltip = LLTrans::getString("InventoryOutboxErrorTooltip"); } @@ -539,19 +631,22 @@ BOOL LLFloaterMarketplaceListings::handleDragAndDrop(S32 x, S32 y, MASK mask, BO return FALSE; } + tooltip_msg = ""; + // 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 no one handled it or it was not accepted and we drop on an empty panel, we try to accept it at the floater level + // as if it was dropped on the marketplace listings root folder + if ((!handled || !isAccepted(*accept)) && !mPanelListings->getVisible() && mRootFolderId.notNull()) { - if (!mPanelListings->getVisible() && mRootFolderId.notNull()) + if (!mPanelListingsSet) { - LLFolderView* root_folder = mPanelListings->getRootFolder(); - handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + setPanels(); } + LLFolderView* root_folder = mPanelListings->getRootFolder(); + handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } return handled; @@ -585,6 +680,15 @@ void LLFloaterMarketplaceListings::onChanged() // LLFloaterAssociateListing //----------------------------------------------------------------------------- +// Tell if a listing has one only version folder +bool hasUniqueVersionFolder(const LLUUID& folder_id) +{ + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, categories, items); + return (categories->size() == 1); +} + LLFloaterAssociateListing::LLFloaterAssociateListing(const LLSD& key) : LLFloater(key) , mUUID() @@ -602,7 +706,7 @@ BOOL LLFloaterAssociateListing::postBuild() { getChild("OK")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::apply, this, TRUE)); getChild("Cancel")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::cancel, this)); - getChild("listing_id")->setPrevalidate(&LLTextValidate::validateNonNegativeS32); + getChild("listing_id")->setPrevalidate(&LLLineEditor::prevalidateNonNegativeS32); center(); return LLFloater::postBuild(); @@ -625,11 +729,12 @@ BOOL LLFloaterAssociateListing::handleKeyHere(KEY key, MASK mask) } // static -void LLFloaterAssociateListing::show(LLFloater* floater, const LLSD& folder_id) +void LLFloaterAssociateListing::show(LLFloaterAssociateListing* floater, const LLSD& folder_id) { - if (!floater) return; + floater->mUUID = folder_id.asUUID(); + floater->open(); } @@ -653,7 +758,7 @@ void LLFloaterAssociateListing::apply(BOOL user_confirm) // Check if the id exists in the merchant SLM DB: note that this record might exist in the LLMarketplaceData // structure even if unseen in the UI, for instance, if its listing_uuid doesn't exist in the merchant inventory LLUUID listing_uuid = LLMarketplaceData::instance().getListingFolder(id); - if (listing_uuid.notNull() && user_confirm && LLMarketplaceData::instance().getActivationState(listing_uuid)) + if (listing_uuid.notNull() && user_confirm && LLMarketplaceData::instance().getActivationState(listing_uuid) && !hasUniqueVersionFolder(mUUID)) { // Look for user confirmation before unlisting LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFloaterAssociateListing::callback_apply, this, _1, _2)); @@ -661,29 +766,30 @@ void LLFloaterAssociateListing::apply(BOOL user_confirm) } // Associate the id with the user chosen folder LLMarketplaceData::instance().associateListing(mUUID,listing_uuid,id); - // Update the folder widgets now that the action is launched - update_marketplace_category(listing_uuid); - update_marketplace_category(mUUID); } else { LLNotificationsUtil::add("AlertMerchantListingInvalidID"); } } - closeFloater(); + close(); } void LLFloaterAssociateListing::cancel() { - closeFloater(); + close(); } //----------------------------------------------------------------------------- // LLFloaterMarketplaceValidation //----------------------------------------------------------------------------- +// Note: The key is the UUID of the folder to validate. +// Validates the whole marketplace listings content if UUID is null. + LLFloaterMarketplaceValidation::LLFloaterMarketplaceValidation(const LLSD& key) -: LLFloater(key), +: LLFloater(), + mKey(key), mEditor(NULL) { //buildFromFile("floater_marketplace_validation.xml"); @@ -699,7 +805,7 @@ BOOL LLFloaterMarketplaceValidation::postBuild() mEditor->setEnabled(FALSE); mEditor->setFocus(TRUE); mEditor->setValue(LLSD()); - + return TRUE; } @@ -714,18 +820,55 @@ void LLFloaterMarketplaceValidation::draw() LLFloater::draw(); } -void LLFloaterMarketplaceValidation::onOpen(const LLSD& key) +void LLFloaterMarketplaceValidation::onOpen(/*const LLSD& key*/) { - // Clear the text panel - mEditor->setValue(LLSD()); + // Clear the messages + clearMessages(); - // Validates the marketplace - LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - if (marketplacelistings_id.notNull()) + // Get the folder UUID to validate. Use the whole marketplace listing if none provided. + LLUUID cat_id(mKey.asUUID()); + if (cat_id.isNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(marketplacelistings_id); - validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2), false); + cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); } + + // Validates the folder + if (cat_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), false); + } + + // Handle the listing folder being processed + handleCurrentListing(); + + // Dump result to the editor panel + if (mEditor) + { + mEditor->setValue(LLSD()); + if (mMessages.empty()) + { + // Display a no error message + mEditor->appendText(LLTrans::getString("Marketplace Validation No Error"), false, false); + } + else + { + // Print out all the messages to the panel + message_list_t::iterator mCurrentLine = mMessages.begin(); + bool new_line = false; + while (mCurrentLine != mMessages.end()) + { + // Errors are printed in bold, other messages in normal font + LLStyleSP style(new LLStyle); + style->mBold = mCurrentLine->mErrorLevel == LLError::LEVEL_ERROR; + mEditor->appendText(mCurrentLine->mMessage, false, new_line, style); + new_line = true; + mCurrentLine++; + } + } + } + // We don't need the messages anymore + clearMessages(); } // static @@ -733,23 +876,53 @@ void LLFloaterMarketplaceValidation::onOK( void* userdata ) { // destroys this object LLFloaterMarketplaceValidation* self = (LLFloaterMarketplaceValidation*) userdata; - self->closeFloater(); + self->clearMessages(); + self->close(); } -void LLFloaterMarketplaceValidation::appendMessage(std::string& message, LLError::ELevel log_level) +void LLFloaterMarketplaceValidation::appendMessage(std::string& message, S32 depth, LLError::ELevel log_level) { - if (mEditor) + // Dump previous listing messages if we're starting a new listing + if (depth == 1) { - // Errors are printed in bold, other messages in normal font - LLStyle::Params style; - LLFontDescriptor new_desc(mEditor->getFont()->getFontDesc()); - new_desc.setStyle(log_level == LLError::LEVEL_ERROR ? LLFontGL::BOLD : LLFontGL::NORMAL); - LLFontGL* new_font = LLFontGL::getFont(new_desc); - style.font = new_font; - mEditor->appendText(message, true, style); + handleCurrentListing(); } + + // Store the message in the current listing message list + Message current_message; + current_message.mErrorLevel = log_level; + current_message.mMessage = message; + mCurrentListingMessages.push_back(current_message); + mCurrentListingErrorLevel = (mCurrentListingErrorLevel < log_level ? log_level : mCurrentListingErrorLevel); } +// Move the current listing messages to the general list if needs be and reset the current listing data +void LLFloaterMarketplaceValidation::handleCurrentListing() +{ + // Dump the current folder messages to the general message list if level warrants it + if (mCurrentListingErrorLevel > LLError::LEVEL_INFO) + { + message_list_t::iterator mCurrentLine = mCurrentListingMessages.begin(); + while (mCurrentLine != mCurrentListingMessages.end()) + { + mMessages.push_back(*mCurrentLine); + mCurrentLine++; + } + } + + // Reset the current listing + mCurrentListingMessages.clear(); + mCurrentListingErrorLevel = LLError::LEVEL_INFO; +} + +void LLFloaterMarketplaceValidation::clearMessages() +{ + mMessages.clear(); + mCurrentListingMessages.clear(); + mCurrentListingErrorLevel = LLError::LEVEL_INFO; +} + +/* Singu Note: What even is this? Where is this used?! //----------------------------------------------------------------------------- // LLFloaterItemProperties //----------------------------------------------------------------------------- @@ -773,10 +946,10 @@ BOOL LLFloaterItemProperties::postBuild() return LLFloater::postBuild(); } -void LLFloaterItemProperties::onOpen(/*const LLSD& key*/) +void LLFloaterItemProperties::onOpen(const LLSD& key) { // Tell the panel which item it needs to visualize LLPanel* panel = getChild("item_panel"); - panel->setItemID(key["id"].asUUID()); + panel->setItemID(mKey["id"].asUUID()); } - +*/ diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h index e948b141d..451034d74 100644 --- a/indra/newview/llfloatermarketplacelistings.h +++ b/indra/newview/llfloatermarketplacelistings.h @@ -52,8 +52,14 @@ class LLPanelMarketplaceListings : public LLPanel public: LLPanelMarketplaceListings(); 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 draw(); LLFolderView* getRootFolder() { return mRootFolder; } + bool allowDropOnRoot(); void buildAllPanels(); @@ -69,19 +75,21 @@ private: void onTabChange(); void onFilterEdit(const std::string& search_string); + void setSortOrder(U32 sort_order); + LLFolderView* mRootFolder; LLButton* mAuditBtn; LLFilterEditor* mFilterEditor; std::string mFilterSubString; - LLInventoryFilter::ESortOrderType mSortOrder; - LLInventoryFilter::EFilterType mFilterType; + bool mFilterListingFoldersOnly; + U32 mSortOrder; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFloaterMarketplaceListings //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLFloaterMarketplaceListings : public LLFloater, public LLFloaterSingleton +class LLFloaterMarketplaceListings : public LLFloater, public LLFloaterSingleton { public: LLFloaterMarketplaceListings(const LLSD& key); @@ -103,13 +111,14 @@ public: void onMouseLeave(S32 x, S32 y, MASK mask); protected: - void setup(); + void setRootFolder(); + void setPanels(); void fetchContents(); void setStatusString(const std::string& statusString); void onClose(bool app_quitting); - void onOpen(const LLSD& key); + void onOpen(/*const LLSD& key*/); void onFocusReceived(); void onChanged(); @@ -131,15 +140,15 @@ private: LLUUID mRootFolderId; LLPanelMarketplaceListings * mPanelListings; - bool mFirstViewListings; + bool mPanelListingsSet; }; //----------------------------------------------------------------------------- // LLFloaterAssociateListing //----------------------------------------------------------------------------- -class LLFloaterAssociateListing : public LLFloater, public LLUISingleton +class LLFloaterAssociateListing : public LLFloater, public LLUISingleton { -friend class LLFloaterReg; +friend class LLUISingleton; public: virtual BOOL postBuild(); virtual BOOL handleKeyHere(KEY key, MASK mask); @@ -163,6 +172,8 @@ private: //----------------------------------------------------------------------------- // LLFloaterMarketplaceValidation //----------------------------------------------------------------------------- +// Note: The key is the UUID of the folder to validate. Validates the whole +// marketplace listings content if UUID is null. // 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 @@ -175,12 +186,27 @@ public: virtual BOOL postBuild(); virtual void draw(); - virtual void onOpen(const LLSD& key); + virtual void onOpen(/*const LLSD& key*/); - void appendMessage(std::string& message, LLError::ELevel log_level); + void clearMessages(); + void appendMessage(std::string& message, S32 depth, LLError::ELevel log_level); static void onOK( void* userdata ); private: + struct Message { + LLError::ELevel mErrorLevel; + std::string mMessage; + }; + typedef std::vector message_list_t; + + void handleCurrentListing(); + + message_list_t mCurrentListingMessages; + LLError::ELevel mCurrentListingErrorLevel; + + message_list_t mMessages; + + LLSD mKey; LLTextEditor* mEditor; }; @@ -195,10 +221,9 @@ public: virtual ~LLFloaterItemProperties(); BOOL postBuild(); - virtual void onOpen(const LLSD& key); + virtual void onOpen(/*const LLSD& key*/); private: }; #endif // LL_LLFLOATERMARKETPLACELISTINGS_H - diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp index 796d6dea2..988be0e1b 100644 --- a/indra/newview/llfloateroutbox.cpp +++ b/indra/newview/llfloateroutbox.cpp @@ -293,7 +293,7 @@ void LLFloaterOutbox::setupOutbox() // Set the sort order newest to oldest inventory_panel->setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); - inventory_panel->getFilter()->markDefault(); + inventory_panel->getFilter().markDefault(); // Get the content of the outbox fetchOutboxContents(); diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 7f3f9d71f..657d5cfdc 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -588,7 +588,7 @@ void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) void LLFolderViewItem::openItem( void ) { if (!mListener) return; - if (mAllowWear || mListener->isItemWearable()) + if (mAllowOpen || mListener->isItemWearable()) { mListener->openItem(); } @@ -1771,7 +1771,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo { return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1790,7 +1790,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1812,7 +1812,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1831,7 +1831,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -2878,21 +2878,21 @@ bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolde } else if (mFoldersByWeight) { - S32 weight_a = compute_stock_count(a->getUUID()); - S32 weight_b = compute_stock_count(b->getUUID()); + S32 weight_a = compute_stock_count(a->getListener()->getUUID()); + S32 weight_b = compute_stock_count(b->getListener()->getUUID()); if (weight_a == weight_b) { // Equal weight -> use alphabetical order - return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); + return (LLStringUtil::compareDict(a->getListener()->getDisplayName(), b->getListener()->getDisplayName()) < 0); } - else if (weight_a == -1) + else if (weight_a == COMPUTE_STOCK_INFINITE) { - // No weight -> move a at the end of the list + // No stock -> move a at the end of the list return false; } - else if (weight_b == -1) + else if (weight_b == COMPUTE_STOCK_INFINITE) { - // No weight -> move b at the end of the list + // No stock -> move b at the end of the list return true; } else diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index 00c11badb..a0246666d 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -141,8 +141,8 @@ protected: BOOL mIsLoading; LLTimer mTimeSinceRequestStart; bool mShowLoadStatus; - bool mAllowWear; bool mAllowDrop; + bool mAllowOpen; std::string mSearchable; U32 mSearchType; @@ -244,8 +244,8 @@ public: void setShowLoadStatus(bool status) { mShowLoadStatus = status; } - void setAllowWear(bool allow) { mAllowWear = allow; } void setAllowDrop(bool allow) { mAllowDrop = allow; } + void setAllowOpen(bool allow) { mAllowOpen = allow; } // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index b2d30af3a..6d688edc6 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -44,6 +44,7 @@ #include "llinventorymodelbackgroundfetch.h" #include "llinventorypanel.h" #include "llmakeoutfitdialog.h" +#include "llmarketplacefunctions.h" #include "llnotificationsutil.h" #include "llpanelmaininventory.h" #include "llpanelobjectinventory.h" @@ -63,6 +64,7 @@ using namespace LLOldEvents; namespace LLInventoryAction { void callback_doToSelected(const LLSD& notification, const LLSD& response, LLFolderView* folder, const std::string& action); + void callback_copySelected(const LLSD& notification, const LLSD& response, class LLFolderView* root, const std::string& action); bool doToSelected(LLFolderView* root, std::string action, BOOL user_confirm = TRUE); void buildMarketplaceFolders(LLFolderView* root); @@ -74,15 +76,26 @@ typedef LLMemberListener object_inventory_listener_t; typedef LLMemberListener inventory_listener_t; typedef LLMemberListener inventory_panel_listener_t; -std::list LLInventoryAction::sMarketplaceFolders; - // Callback for doToSelected if DAMA required... void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LLSD& response, LLFolderView* folder, const std::string& action) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) // YES { - doToSelected(folder, action, FALSE); + doToSelected(folder, action); + } +} + +void LLInventoryAction::callback_copySelected(const LLSD& notification, const LLSD& response, class LLFolderView* root, const std::string& action) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES, Move no copy item(s) + { + doToSelected(root, "copy_or_move_to_marketplace_listings"); + } + else if (option == 1) // NO, Don't move no copy item(s) (leave them behind) + { + doToSelected(root, "copy_to_marketplace_listings"); } } @@ -105,7 +118,7 @@ bool LLInventoryAction::doToSelected(LLFolderView* root, std::string action, BOO { // Wearing an object from any listing, active or not, is verbotten LLNotificationsUtil::add("AlertMerchantListingCannotWear"); - return; + return true; } // Note: we do not prompt for change when opening items (e.g. textures or note cards) on the marketplace... } @@ -116,13 +129,30 @@ bool LLInventoryAction::doToSelected(LLFolderView* root, std::string action, BOO if ((("cut" == action) || ("delete" == action)) && (LLMarketplaceData::instance().isListed(*set_iter) || LLMarketplaceData::instance().isVersionFolder(*set_iter))) { // Cut or delete of the active version folder or listing folder itself will unlist the listing so ask that question specifically - LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); - return; + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, root, action)); + return true; } // Any other case will simply modify but not unlist a listing - LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); - return; + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, root, action)); + return true; } + // Cutting or deleting a whole listing needs confirmation as SLM will be archived and inaccessible to the user + else if (LLMarketplaceData::instance().isListed(*set_iter) && (("cut" == action) || ("delete" == action))) + { + LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, root, action)); + return true; + } + } + } + // Copying to the marketplace needs confirmation if nocopy items are involved + if (("copy_to_marketplace_listings" == action)) + { + std::set::iterator set_iter = selected_items.begin(); + bool contains_nocopy_items(const LLUUID& id); + if (contains_nocopy_items(*set_iter)) + { + LLNotificationsUtil::add("ConfirmCopyToMarketplace", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_copySelected, _1, _2, root, action)); + return true; } } @@ -206,6 +236,7 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) // containing folder will need to be updated as well as their initially containing folder. For // instance, moving a stock folder from a listed folder to another will require an update of the // target listing *and* the original listing. So we need to keep track of both. + // Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt. sMarketplaceFolders.clear(); const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); std::set selected_items = root->getSelectionList(); @@ -214,8 +245,16 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) const LLInventoryObject* obj(gInventory.getObject(*set_iter)); if (gInventory.isObjectDescendentOf(obj->getParentUUID(), marketplacelistings_id)) { - sMarketplaceFolders.push_back(obj->getParentUUID()); - sMarketplaceFolders.push_back(obj->getUUID()); + const LLUUID& parent_id = obj->getParentUUID(); + if (parent_id != marketplacelistings_id) + { + sMarketplaceFolders.push_back(parent_id); + } + const LLUUID& curr_id = obj->getUUID(); + if (curr_id != marketplacelistings_id) + { + sMarketplaceFolders.push_back(curr_id); + } } } // Suppress dupes in the list so we wo't update listings twice diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 492694938..d528fee6c 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -116,7 +116,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*); bool confirm_attachment_rez(const LLSD& notification, const LLSD& response); static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); -void build_context_menu_folder_options(LLInventoryModel* mModel, const LLUUID& mUUID, menuentry_vec_t& mItems, menuentry_vec_t& mDisabledItems); +void build_context_menu_folder_options(LLInventoryModel* mModel, const LLUUID& mUUID, menuentry_vec_t& items, menuentry_vec_t& disabled_items); // Helper functions @@ -203,7 +203,6 @@ const std::string& LLInvFVBridge::getDisplayName() const { buildDisplayName(); } - return mDisplayName; } @@ -273,6 +272,7 @@ BOOL LLInvFVBridge::cutToClipboard() { const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const BOOL cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id); + if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) || LLMarketplaceData::instance().isListedAndActive(mUUID))) { @@ -288,6 +288,7 @@ BOOL LLInvFVBridge::cutToClipboard() return FALSE; } +// Callback for cutToClipboard if DAMA required... BOOL LLInvFVBridge::callback_cutToClipboard(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -328,7 +329,7 @@ void LLInvFVBridge::showProperties() LLFloater* floater_properties = LLFloaterProperties::find(mUUID, LLUUID::null); if (floater_properties) { - floater_properties->setVisibleAndFrontmost(); + floater_properties->open()/*setVisibleAndFrontmost()*/; } } else @@ -350,7 +351,7 @@ void LLInvFVBridge::removeBatch(std::vector& batch) U32 i,j; for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if (item) @@ -363,7 +364,7 @@ void LLInvFVBridge::removeBatch(std::vector& batch) } for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if (cat) @@ -404,7 +405,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector& // being deleted. for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -417,7 +418,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector& for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -458,7 +459,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector& for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if(cat) @@ -539,7 +540,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const { const LLUUID &item_id = objects.at(i); - // Folders are pasteable if all items in there are copyable + // Folders are pastable if all items in there are copyable const LLInventoryCategory *cat = model->getCategory(item_id); if (cat) { @@ -550,7 +551,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const continue; } - // Each item must be copyable to be pasteable + // Each item must be copyable to be pastable LLItemBridge item_br(panel, mRoot, item_id); if (!item_br.isItemCopyable()) return FALSE; @@ -819,14 +820,27 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Cut")); } - if (canListOnMarketplace() && !isMarketplaceListingsFolder()) + if (canListOnMarketplace() && !isMarketplaceListingsFolder() && !isInboxFolder()) { items.push_back(std::string("Marketplace Separator")); - items.push_back(std::string("Merchant Copy")); - if (!canListOnMarketplaceNow()) + if (gMenuHolder->getChild("MerchantOutbox")->getVisible()) { - disabled_items.push_back(std::string("Merchant Copy")); + items.push_back(std::string("Merchant Copy")); + if (!canListOnOutboxNow()) + { + disabled_items.push_back(std::string("Merchant Copy")); + } + } + if (gMenuHolder->getChild("MarketplaceListings")->getVisible()) + { + items.push_back(std::string("Marketplace Copy")); + items.push_back(std::string("Marketplace Move")); + if (!canListOnMarketplaceNow()) + { + disabled_items.push_back(std::string("Marketplace Copy")); + disabled_items.push_back(std::string("Marketplace Move")); + } } } } @@ -1009,7 +1023,7 @@ void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags, { disabled_items.push_back(std::string("Rename")); } - + // Singu TODO: Remove this block when removing outbox. if (isOutboxFolderDirectParent()) { @@ -1033,15 +1047,16 @@ void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, // 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 Check Listing")); items.push_back(std::string("Marketplace List")); items.push_back(std::string("Marketplace Unlist")); - if (LLMarketplaceData::instance().isUpdating(mUUID)) + if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0)) { // During SLM update, disable all marketplace related options + // Also disable all if multiple selected items disabled_items.push_back(std::string("Marketplace Create Listing")); disabled_items.push_back(std::string("Marketplace Associate Listing")); - disabled_items.push_back(std::string("Marketplace Disassociate Listing")); + disabled_items.push_back(std::string("Marketplace Check Listing")); disabled_items.push_back(std::string("Marketplace List")); disabled_items.push_back(std::string("Marketplace Unlist")); } @@ -1074,7 +1089,6 @@ void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, } 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 (gSavedSettings.getBOOL("MarketplaceListingsLogging")) @@ -1092,9 +1106,10 @@ void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, { items.push_back(std::string("Marketplace Activate")); items.push_back(std::string("Marketplace Deactivate")); - if (LLMarketplaceData::instance().isUpdating(mUUID)) + if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0)) { // During SLM update, disable all marketplace related options + // Also disable all if multiple selected items disabled_items.push_back(std::string("Marketplace Activate")); disabled_items.push_back(std::string("Marketplace Deactivate")); } @@ -1115,14 +1130,30 @@ void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, } } } - // 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); LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(listing_folder_id); + + if (depth >= 2) + { + // Prevent creation of new folders if the max count has been reached on this version folder (active or not) + LLUUID local_version_folder_id = nested_parent_id(mUUID,depth-1); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(local_version_folder_id, categories, items, FALSE); + if (categories.size() >= gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + disabled_items.push_back(std::string("New Folder")); + } + } + + // Options available at all levels on items and categories if (!LLMarketplaceData::instance().isListed(listing_folder_id) || version_folder_id.isNull()) { disabled_items.push_back(std::string("Marketplace Edit Listing")); } + // Separator items.push_back(std::string("Marketplace Listings Separator")); } @@ -1242,7 +1273,7 @@ BOOL LLInvFVBridge::isInboxFolder() const return gInventory.isObjectDescendentOf(mUUID, inbox_id); } -BOOL LLInvFVBridge::isMarketplaceListingsFodler() const +BOOL LLInvFVBridge::isMarketplaceListingsFolder() const { const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); @@ -1416,11 +1447,18 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, case LLAssetType::AT_CATEGORY: if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) { - // Create a link folder handler instead. + // Create a link folder handler instead new_listener = new LLLinkFolderBridge(inventory, root, uuid); - break; } - new_listener = new LLFolderBridge(inventory, root, uuid); + else if (actual_asset_type == LLAssetType::AT_MARKETPLACE_FOLDER) + { + // Create a marketplace folder handler + new_listener = new LLMarketplaceFolderBridge(inventory, root, uuid); + } + else + { + new_listener = new LLFolderBridge(inventory, root, uuid); + } break; case LLAssetType::AT_LINK: case LLAssetType::AT_LINK_FOLDER: @@ -1466,7 +1504,7 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) void LLInvFVBridge::removeObject(LLInventoryModel *model, const LLUUID &uuid) { // Keep track of the parent - LLInventoryItem* itemp = mode->getItem(uuid); + LLInventoryItem* itemp = model->getItem(uuid); LLUUID parent_id = (itemp ? itemp->getParentUUID() : LLUUID::null); // Remove the object model->removeObject(uuid); @@ -1511,7 +1549,7 @@ bool LLInvFVBridge::canListOnMarketplace() const { LLInventoryModel * model = getInventoryModel(); - const LLViewerInventoryCategory * cat = model->getCategory(mUUID); + LLViewerInventoryCategory * cat = model->getCategory(mUUID); if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { return false; @@ -1522,16 +1560,6 @@ bool LLInvFVBridge::canListOnMarketplace() const return false; } - if (getOutboxFolder().isNull()) - { - return false; - } - - if (isInboxFolder() || isOutboxFolder()) - { - return false; - } - LLViewerInventoryItem * item = model->getItem(mUUID); if (item) { @@ -1549,7 +1577,8 @@ bool LLInvFVBridge::canListOnMarketplace() const return true; } -bool LLInvFVBridge::canListOnMarketplaceNow() const +// *TODO : Suppress canListOnOutboxNow() once we deprecate Merchant Outbox completely +bool LLInvFVBridge::canListOnOutboxNow() const { bool can_list = true; @@ -1597,6 +1626,56 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const return can_list; } +bool LLInvFVBridge::canListOnMarketplaceNow() const +{ + bool can_list = true; + + const LLInventoryObject* obj = getInventoryObject(); + can_list &= (obj != NULL); + + if (can_list) + { + const LLUUID& object_id = obj->getLinkedUUID(); + can_list = object_id.notNull(); + + if (can_list) + { + LLFolderViewFolder * object_folderp = mRoot->getFolderByID(object_id); + if (object_folderp) + { + can_list = !object_folderp->isLoading(); + } + } + + if (can_list) + { + std::string error_msg; + LLInventoryModel* model = getInventoryModel(); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplacelistings_id.notNull()) + { + LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id); + LLInventoryCategory *cat = model->getCategory(mUUID); + if (cat) + { + can_list = can_move_folder_to_marketplace(master_folder, master_folder, cat, error_msg); + } + else + { + LLInventoryItem *item = model->getItem(mUUID); + can_list = (item ? can_move_item_to_marketplace(master_folder, master_folder, item, error_msg) : false); + } + } + else + { + can_list = false; + } + } + } + + return can_list; +} + // +=================================================+ // | InventoryFVBridgeBuilder | @@ -1721,6 +1800,14 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } + else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) + { + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + // Note: For a single item, if it's not a copy, then it's a move + move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action)); + } else if ("marketplace_edit_listing" == action) { std::string url = LLMarketplaceData::instance().getListingURL(mUUID); @@ -1810,7 +1897,6 @@ LLUIImagePtr LLItemBridge::getIcon() const } return LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT); - } LLUIImagePtr LLItemBridge::getIconOverlay() const @@ -1902,9 +1988,7 @@ std::string LLItemBridge::getLabelSuffix() const suffix += NO_XFER; } - BOOL temp = item->getPermissions().getGroup() == gAgent.getID(); - - if (temp) + if (item->getPermissions().getGroup() == gAgent.getID()) { suffix += TEMPO; } @@ -2145,68 +2229,12 @@ 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) + ")"; - } - } - // Add updating suffix - if (LLMarketplaceData::instance().isUpdating(getUUID())) - { - suffix += " (" + LLTrans::getString("MarketplaceUpdating") + ")"; - } - return LLInvFVBridge::getLabelSuffix() + suffix; - } - else - { - return LLInvFVBridge::getLabelSuffix(); - } + return LLInvFVBridge::getLabelSuffix(); } LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const { - if (isMarketplaceListingsFolder() && LLMarketplaceData::instance().getActivationState(getUUID())) - { - return LLFontGL::BOLD; - } - else - { - return LLFontGL::NORMAL; - } + return LLFontGL::NORMAL; } @@ -2437,9 +2465,11 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); - const BOOL move_is_into_outfit = getCategory() && (getCategory()->getPreferredType() == LLFolderType::FT_OUTFIT); + const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType() == LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); //-------------------------------------------------------------------------------- @@ -2447,6 +2477,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, // BOOL is_movable = TRUE; + std::string tooltip_msg; if (is_movable && (marketplacelistings_id == cat_id)) { @@ -2490,6 +2521,12 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, is_movable = FALSE; // tooltip? } + if (is_movable && (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)) + { + // One cannot move a folder into a stock folder + is_movable = FALSE; + // tooltip? + } LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; @@ -2563,11 +2600,6 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, is_movable = RlvFolderLocks::instance().canMoveFolder(cat_id, mUUID); } // [/RLVa:KB] - if (is_movable && move_is_into_marketplacelistings) - { - // One cannot move a folder into a stock folder - is_movable = (getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK); - } if (is_movable && (move_is_into_outbox || move_is_into_marketplacelistings)) { @@ -2614,6 +2646,17 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); return true; } + if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isListed(cat_id)) + { + // Moving a whole listing folder will result in archival of SLM data. Ask confirmation. + LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings) + { + LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } } // Look for any gestures and deactivate them if (move_is_into_trash) @@ -2680,22 +2723,29 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } if (move_is_from_marketplacelistings) { - // If we move from an active (listed) listing, checks that it's still valid, if not, unlist - LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); - if (version_folder_id.notNull()) + // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder) + if (from_folder_uuid == marketplacelistings_id) { - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); - if (!validate_marketplacelistings(cat, NULL)) + // Clear the folder from the marketplace in case it is a listing folder + if (LLMarketplaceData::instance().isListed(cat_id)) { - LLMarketplaceData::instance().activateListing(version_folder_id,false); + LLMarketplaceData::instance().clearListing(cat_id); } } - // Update the listing we moved from anyway - update_marketplace_category(from_folder_uuid); - // Clear the folder from the marketplace in case it is a listing folder - if (LLMarketplaceData::instance().isListed(cat_id)) + else { - LLMarketplaceData::instance().clearListing(cat_id); + // If we move from within an active (listed) listing, check that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + if (!validate_marketplacelistings(cat, NULL)) + { + LLMarketplaceData::instance().activateListing(version_folder_id,false); + } + } + // In all cases, update the listing we moved from so suffix are updated + update_marketplace_category(from_folder_uuid); } } } @@ -3121,7 +3171,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID); LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); mMessage = ""; - if (!validate_marketplacelistings(cat, boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2))) + if (!validate_marketplacelistings(cat, boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3))) { LLSD subs; subs["[ERROR_CODE]"] = mMessage; @@ -3147,7 +3197,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if (depth_nesting_in_marketplace(mUUID) == 1) { - LLMarketplaceData::instance().activateListing(mUUID, false); + LLMarketplaceData::instance().activateListing(mUUID, false,1); } return; } @@ -3156,13 +3206,35 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) if (depth_nesting_in_marketplace(mUUID) == 2) { LLInventoryCategory* category = gInventory.getCategory(mUUID); - LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), LLUUID::null); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), LLUUID::null, 1); } return; } else if ("marketplace_create_listing" == action) { - LLMarketplaceData::instance().createListing(mUUID); + LLViewerInventoryCategory* cat = gInventory.getCategory(mUUID); + mMessage = ""; + bool validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false); + if (!validates) + { + mMessage = ""; + validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),true); + if (validates) + { + LLNotificationsUtil::add("MerchantForceValidateListing"); + } + } + + if (!validates) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().createListing(mUUID); + } return; } else if ("marketplace_disassociate_listing" == action) @@ -3178,7 +3250,13 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) } else if ("marketplace_associate_listing" == action) { - LLFloaterAssociateListing::show(mUUID); + LLFloaterAssociateListing::showInstance(mUUID); + return; + } + else if ("marketplace_check_listing" == action) + { + LLSD data(mUUID); + new LLFloaterMarketplaceValidation(data); // new.. but floaters clean themselves up. return; } else if ("marketplace_edit_listing" == action) @@ -3214,6 +3292,13 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); } + else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) + { + LLInventoryCategory * cat = gInventory.getCategory(mUUID); + if (!cat) return; + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("copy_to_marketplace_listings" == action), (("copy_or_move_to_marketplace_listings" == action))); + } // Singu TODO: Remove this block when removing outbox. else if ("send_to_marketplace" == action) { @@ -3222,14 +3307,14 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) } } -void LLFolderBridge::gatherMessage(std::string& message, LLError::ELevel log_level) +void LLFolderBridge::gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level) { if (log_level >= LLError::LEVEL_ERROR) { if (!mMessage.empty()) { // Currently, we do not gather all messages as it creates very long alerts - // Users can get to the whole list of errors on a listing using the "Check for Errors" audit button + // Users can get to the whole list of errors on a listing using the "Check for Errors" audit button or "Check listing" right click menu //mMessage += "\n"; return; } @@ -3310,7 +3395,7 @@ LLUIImagePtr LLFolderBridge::getIcon() const return getFolderIcon(FALSE); } -LLUIImagePtr LLFolderBridge::getOpenIcon() const +LLUIImagePtr LLFolderBridge::getIconOpen() const { return getFolderIcon(TRUE); } @@ -3324,12 +3409,6 @@ LLUIImagePtr LLFolderBridge::getFolderIcon(BOOL is_open) const // We override the type when in the marketplace listings folder and only for version folder preferred_type = LLFolderType::FT_MARKETPLACE_VERSION; } - else if ((preferred_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth == -1)) - { - // We override the type when a stock folder is outside of the marketplace listings root - // as we don't want to export that notion outside of marketplace - preferred_type = LLFolderType::FT_NONE; - } return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open)); } @@ -3426,27 +3505,27 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) if (paste_into_marketplacelistings && !LLMarketplaceData::instance().isListed(mUUID) && LLMarketplaceData::instance().isInActiveFolder(mUUID)) { // Prompt the user if pasting in a marketplace active version listing (note that pasting right under the listing folder root doesn't need a prompt) - LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_pasteFromClipboard, this, _1, _2)); + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_pasteFromClipboard, this, _1, _2, only_copies)); } else { // Otherwise just do the paste - perform_pasteFromClipboard(); + perform_pasteFromClipboard(only_copies); } } } // Callback for pasteFromClipboard if DAMA required... -void LLFolderBridge::callback_pasteFromClipboard(const LLSD& notification, const LLSD& response) +void LLFolderBridge::callback_pasteFromClipboard(const LLSD& notification, const LLSD& response, bool only_copies) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) // YES { - perform_pasteFromClipboard(); + perform_pasteFromClipboard(only_copies); } } -void LLFolderBridge::perform_pasteFromClipboard() +void LLFolderBridge::perform_pasteFromClipboard(bool only_copies) { LLInventoryModel* model = getInventoryModel(); if (model && isClipboardPasteable()) @@ -3465,25 +3544,27 @@ void LLFolderBridge::perform_pasteFromClipboard() std::vector objects; LLInventoryClipboard::instance().retrieve(objects); + LLViewerInventoryCategory* dest_folder = getCategory(); if (move_is_into_outbox || move_is_into_marketplacelistings) { std::string error_msg; const LLViewerInventoryCategory* master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); - LLViewerInventoryCategory* dest_folder = getCategory(); - for (LLDynamicArray::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + int index = 0; + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { const LLUUID& item_id = (*iter); LLInventoryItem *item = model->getItem(item_id); LLInventoryCategory *cat = model->getCategory(item_id); - if (item && !can_move_item_to_marketplace(master_folder, dest_folder, item, error_msg, objects.size(), true)) + if (item && !can_move_item_to_marketplace(master_folder, dest_folder, item, error_msg, objects.size() - index, true)) { break; } - if (cat && !can_move_folder_to_marketplace(master_folder, dest_folder, cat, error_msg, objects.size(), true, true)) + if (cat && !can_move_folder_to_marketplace(master_folder, dest_folder, cat, error_msg, objects.size() - index, true, true)) { break; } + ++index; } if (!error_msg.empty()) { @@ -3493,6 +3574,25 @@ void LLFolderBridge::perform_pasteFromClipboard() return; } } + else + { + // Check that all items can be moved into that folder : for the moment, only stock folder mismatch is checked + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + LLInventoryCategory *cat = model->getCategory(item_id); + + if ((item && !dest_folder->acceptItem(item)) || (cat && (dest_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK))) + { + std::string error_msg = LLTrans::getString("TooltipOutboxMixedStock"); + LLSD subs; + subs["[ERROR_CODE]"] = error_msg; + LLNotificationsUtil::add("StockPasteFailed", subs); + return; + } + } + } const LLUUID parent_id(mUUID); @@ -3577,7 +3677,7 @@ void LLFolderBridge::perform_pasteFromClipboard() { if (move_is_into_marketplacelistings) { - move_folder_into_marketplacelistings(vicat, parent_id, true); + move_folder_to_marketplacelistings(vicat, parent_id, true); } else { @@ -3725,27 +3825,32 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items } if (isMarketplaceListingsFolder()) { - addMarketplaceContextMenuOptions(flags, mItems, mDisabledItems); + addMarketplaceContextMenuOptions(flags, items, disabled_items); if (LLMarketplaceData::instance().isUpdating(mUUID)) { - mDisabledItems.push_back(std::string("New Folder")); - mDisabledItems.push_back(std::string("Rename")); - mDisabledItems.push_back(std::string("Cut")); - mDisabledItems.push_back(std::string("Copy")); - mDisabledItems.push_back(std::string("Paste")); - mDisabledItems.push_back(std::string("Delete")); - } - else if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) - { - mDisabledItems.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("Rename")); + disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("Copy")); + disabled_items.push_back(std::string("Paste")); + disabled_items.push_back(std::string("Delete")); } } + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Script")); + disabled_items.push_back(std::string("New Note")); + disabled_items.push_back(std::string("New Gesture")); + disabled_items.push_back(std::string("New Clothes")); + disabled_items.push_back(std::string("New Body Parts")); + } if (marketplace_listings_id == mUUID) { - mDisabledItems.push_back(std::string("New Folder")); - mDisabledItems.push_back(std::string("Rename")); - mDisabledItems.push_back(std::string("Cut")); - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("Rename")); + disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("Delete")); } if(trash_id == mUUID) { @@ -3909,11 +4014,14 @@ void build_context_menu_folder_options(LLInventoryModel* model, const LLUUID& mU if (trash_id == mUUID) return; if (model->isObjectDescendentOf(mUUID, trash_id)) return; - if (!isItemRemovable()) + if (!get_is_item_removable(model, mUUID)) { disabled_items.push_back(std::string("Delete")); } - if (isMarketplaceListingsFolder()) return; + + // copied from ::isMarketplaceListingsFolder + const LLUUID listings_folder = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (listings_folder.notNull() && gInventory.isObjectDescendentOf(mUUID, listings_folder)) return; items.push_back(std::string("Open Folder In New Window")); @@ -4209,6 +4317,124 @@ void LLFolderBridge::modifyOutfit(BOOL append/*, BOOL replace*/) LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, append ); } + +// +=================================================+ +// | LLMarketplaceFolderBridge | +// +=================================================+ + +// LLMarketplaceFolderBridge is a specialized LLFolderBridge for use in Marketplace Inventory panels +LLMarketplaceFolderBridge::LLMarketplaceFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : +LLFolderBridge(inventory, root, uuid) +{ + m_depth = depth_nesting_in_marketplace(mUUID); + m_stockCountCache = COMPUTE_STOCK_NOT_EVALUATED; +} + +LLUIImagePtr LLMarketplaceFolderBridge::getIcon() const +{ + return getMarketplaceFolderIcon(FALSE); +} + +LLUIImagePtr LLMarketplaceFolderBridge::getIconOpen() const +{ + return getMarketplaceFolderIcon(TRUE); +} + +LLUIImagePtr LLMarketplaceFolderBridge::getMarketplaceFolderIcon(BOOL is_open) const +{ + LLFolderType::EType preferred_type = getPreferredType(); + if (!LLMarketplaceData::instance().isUpdating(getUUID())) + { + // Skip computation (expensive) if we're waiting for updates. Use the old value in that case. + m_depth = depth_nesting_in_marketplace(mUUID); + } + if ((preferred_type == LLFolderType::FT_NONE) && (m_depth == 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, is_open)); +} + +std::string LLMarketplaceFolderBridge::getLabelSuffix() const +{ + /* Singu TODO? + static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); + + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) + { + return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); + } + */ + + 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 + bool updating = LLMarketplaceData::instance().isUpdating(getUUID()); + if (!updating) + { + // Skip computation (expensive) if we're waiting for update anyway. Use the old value in that case. + m_stockCountCache = compute_stock_count(getUUID()); + } + if (m_stockCountCache == 0) + { + suffix += " (" + LLTrans::getString("MarketplaceNoStock") + ")"; + } + else if (m_stockCountCache != COMPUTE_STOCK_INFINITE) + { + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + suffix += " (" + LLTrans::getString("MarketplaceStock"); + } + else + { + suffix += " (" + LLTrans::getString("MarketplaceMax"); + } + if (m_stockCountCache == COMPUTE_STOCK_NOT_EVALUATED) + { + suffix += "=" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + else + { + suffix += "=" + llformat("%d", m_stockCountCache) + ")"; + } + } + // Add updating suffix + if (updating) + { + suffix += " (" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + return LLInvFVBridge::getLabelSuffix() + suffix; +} + +LLFontGL::StyleFlags LLMarketplaceFolderBridge::getLabelStyle() const +{ + return (LLMarketplaceData::instance().getActivationState(getUUID()) ? LLFontGL::BOLD : LLFontGL::NORMAL); +} + + + + // helper stuff bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv* move_inv) { @@ -4218,7 +4444,7 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response if(option == 0 && object) { - if (cat_and_wear && cat_and_wear->mWear) + if (cat_and_wear && cat_and_wear->mWear) // && !cat_and_wear->mFolderResponded) { LLInventoryObject::object_list_t inventory_objects; object->getInventoryContents(inventory_objects); @@ -4366,12 +4592,15 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; + LLInventoryFilter* filter = getInventoryFilter(); + if (!filter) return false; + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); 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 &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const LLUUID from_folder_uuid = inv_item->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); @@ -4452,37 +4681,46 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, //-------------------------------------------------------------------------------- // Determine if item can be moved & dropped - // + // Note: if user_confirm is false, we already went through those accept logic test and can skip them accept = TRUE; - if (!is_movable) + if (user_confirm && !is_movable) { accept = FALSE; } - else if ((mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) + else if (user_confirm && (mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) { accept = FALSE; } - else if (move_is_into_current_outfit || move_is_into_outfit) + else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit)) { accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); } - else if (move_is_into_favorites || move_is_into_landmarks) + else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks)) { accept = can_move_to_landmarks(inv_item); } - else if (move_is_into_outbox || move_is_into_marketplacelistings) + else if (user_confirm && (move_is_into_outbox || move_is_into_marketplacelistings)) { const LLViewerInventoryCategory* master_folder = (move_is_into_outbox ? model->getFirstDescendantOf(outbox_id, mUUID) : model->getFirstDescendantOf(marketplacelistings_id, mUUID)); LLViewerInventoryCategory* dest_folder = getCategory(); - accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, 1/*LLToolDragAndDrop::instance().getCargoCount()*/); + std::string tooltip_msg; + accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex()); + } + + // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility) + if (user_confirm && accept) + { + LLViewerInventoryCategory * dest_folder = getCategory(); + accept = dest_folder->acceptItem(inv_item); } LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); // Check whether the item being dragged from active inventory panel - if (accept && active_panel) + // passes the filter of the destination panel. + if (user_confirm && accept && active_panel) { LLFolderView* active_folder_view = active_panel->getRootFolder(); if (!active_folder_view) return false; @@ -4501,7 +4739,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // and then select any item that is dropped only in the panel that it is dropped in if (active_panel && (destination_panel != active_panel)) { - active_panel->unSelectAll(); + active_panel->unSelectAll(); } // Dropping in or out of marketplace needs (sometimes) confirmation if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) @@ -4513,6 +4751,11 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); return true; } + if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings) + { + LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); + return true; + } } //-------------------------------------------------------------------------------- @@ -4593,8 +4836,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLMarketplaceData::instance().activateListing(version_folder_id, false); } } - // Update the listing we moved from anyway - update_marketplace_category(from_folder_uuid); } // @@ -5309,6 +5550,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")); @@ -5481,7 +5728,7 @@ void LLNotecardBridge::openItem() void LLNotecardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - lldebugs << "LLNotecardBridge::buildContextMenu()" << LL_ENDL; + LL_DEBUGS() << "LLNotecardBridge::buildContextMenu()" << LL_ENDL; if (isMarketplaceListingsFolder()) { @@ -6704,15 +6951,6 @@ void LLMeshBridge::openItem() } } -void LLMeshBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - // preview mesh - } -} - void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { LL_DEBUGS() << "LLMeshBridge::buildContextMenu()" << LL_ENDL; diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 76b10f03a..56bc2e539 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -72,6 +72,7 @@ public: bool canShare() const; bool canListOnMarketplace() const; + bool canListOnOutboxNow() const; bool canListOnMarketplaceNow() const; //-------------------------------------------------------------------- @@ -156,6 +157,7 @@ protected: BOOL isAgentInventory() const; // false if lost or in the inventory library BOOL isCOFFolder() const; // true if COF or descendant of BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox + BOOL isOutboxFolderDirectParent() const; BOOL isMarketplaceListingsFolder() const; // true if descendant of Marketplace listings folder const LLUUID getOutboxFolder() const; @@ -175,6 +177,9 @@ public: BOOL callback_cutToClipboard(const LLSD& notification, const LLSD& response); BOOL perform_cutToClipboard(); +public: + BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox + protected: LLHandle mInventoryPanel; LLFolderView* mRoot; @@ -273,9 +278,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual LLUIImagePtr getIconOpen() const; virtual LLUIImagePtr getIconOverlay() const; - static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); - virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; @@ -346,10 +349,10 @@ public: static LLHandle sSelf; static void staticFolderOptionsMenu(); -private: - void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response); - void perform_pasteFromClipboard(); - void gatherMessage(std::string& message, LLError::ELevel log_level); +protected: + void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response, bool only_copies); + void perform_pasteFromClipboard(bool only_copies); + void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level); LLUIImagePtr getFolderIcon(BOOL is_open) const; bool mCallingCards; @@ -586,7 +589,6 @@ class LLMeshBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: @@ -654,7 +656,6 @@ class LLRecentInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder { public: LLRecentInventoryBridgeBuilder() {} - // Overrides FolderBridge for Recent Inventory Panel. // It use base functionality for bridges other than FolderBridge. virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, @@ -666,6 +667,31 @@ public: U32 flags = 0x00) const; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Marketplace Inventory Panel related classes +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMarketplaceFolderBridge : public LLFolderBridge +{ +public: + // Overloads some display related methods specific to folders in a marketplace floater context + LLMarketplaceFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid); + + virtual LLUIImagePtr getIcon() const; + virtual LLUIImagePtr getIconOpen() const; + virtual std::string getLabelSuffix() const; + virtual LLFontGL::StyleFlags getLabelStyle() const; + +private: + LLUIImagePtr getMarketplaceFolderIcon(BOOL is_open) const; + // Those members are mutable because they are cached variables to speed up display, not a state variables + mutable S32 m_depth; + mutable S32 m_stockCountCache; +}; + + void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace = false); diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 38b83fcc5..8e9c4fdf0 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -158,10 +158,29 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const // Marketplace folder filtering const U32 filterTypes = mFilterOps.mFilterTypes; - const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | FILTERTYPE_MARKETPLACE_UNASSOCIATED; + const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | + FILTERTYPE_MARKETPLACE_UNASSOCIATED | FILTERTYPE_MARKETPLACE_LISTING_FOLDER | + FILTERTYPE_NO_MARKETPLACE_ITEMS; if (filterTypes & marketplace_filter) { S32 depth = depth_nesting_in_marketplace(folder_id); + + if (filterTypes & FILTERTYPE_NO_MARKETPLACE_ITEMS) + { + if (depth >= 0) + { + return false; + } + } + + if (filterTypes & FILTERTYPE_MARKETPLACE_LISTING_FOLDER) + { + if (depth > 1) + { + return false; + } + } + if (depth > 0) { LLUUID listing_uuid = nested_parent_id(folder_id, depth); @@ -179,7 +198,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return false; } } - else if (filterTypes & FILTER_MARKETPLACE_UNASSOCIATED) + else if (filterTypes & FILTERTYPE_MARKETPLACE_UNASSOCIATED) { if (LLMarketplaceData::instance().isListed(listing_uuid)) { @@ -494,6 +513,25 @@ void LLInventoryFilter::setFilterMarketplaceUnassociatedFolders() mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_UNASSOCIATED; } +void LLInventoryFilter::setFilterMarketplaceListingFolders(bool select_only_listing_folders) +{ + if (select_only_listing_folders) + { + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_LISTING_FOLDER; + setModified(FILTER_MORE_RESTRICTIVE); + } + else + { + mFilterOps.mFilterTypes &= ~FILTERTYPE_MARKETPLACE_LISTING_FOLDER; + setModified(FILTER_LESS_RESTRICTIVE); + } +} + +void LLInventoryFilter::setFilterNoMarketplaceFolder() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_NO_MARKETPLACE_ITEMS; +} + void LLInventoryFilter::setFilterUUID(const LLUUID& object_id) { if (mFilterOps.mFilterUUID == object_id) diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 352d0fae7..73b564ff1 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -58,10 +58,12 @@ 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 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 + 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 + FILTERTYPE_MARKETPLACE_LISTING_FOLDER = 0x1 << 9, // pass iff folder is a listing folder + FILTERTYPE_NO_MARKETPLACE_ITEMS = 0x1 << 10 // pass iff folder is not under the marketplace }; enum EFilterLink @@ -118,6 +120,8 @@ struct FilterOps void setFilterMarketplaceActiveFolders(); void setFilterMarketplaceInactiveFolders(); void setFilterMarketplaceUnassociatedFolders(); + void setFilterMarketplaceListingFolders(bool select_only_listing_folders); + void setFilterNoMarketplaceFolder(); 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 ccc466bab..c214351f1 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -37,6 +37,7 @@ #include "llinventorydefines.h" #include "llsdserialize.h" #include "llspinctrl.h" +#include "lltrans.h" #include "llui.h" #include "message.h" @@ -45,6 +46,7 @@ #include "llappviewer.h" //#include "llfirstuse.h" #include "llfloaterinventory.h" +#include "llfloatermarketplacelistings.h" #include "llfloateroutbox.h" #include "llfocusmgr.h" #include "llfolderview.h" @@ -113,6 +115,21 @@ S32 count_copyable_items(LLInventoryModel::item_array_t& items) return count; } +// Helper function : Count only the non-copyable items, i.e. the stock items, skip the others +S32 count_stock_items(LLInventoryModel::item_array_t& items) +{ + S32 count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLViewerInventoryItem* item = *it; + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + count++; + } + } + return count; +} + // Helper function : Count the number of stock folders S32 count_stock_folders(LLInventoryModel::cat_array_t& categories) { @@ -128,6 +145,72 @@ S32 count_stock_folders(LLInventoryModel::cat_array_t& categories) return count; } +// Helper funtion : Count the number of items (not folders) in the descending hierarchy +S32 count_descendants_items(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); + + S32 count = item_array->size(); + + 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++) + { + LLViewerInventoryCategory* category = *iter; + count += count_descendants_items(category->getUUID()); + } + + return count; +} + +// Helper function : Returns true if the hierarchy contains nocopy items +bool contains_nocopy_items(const LLUUID& id) +{ + LLInventoryCategory* cat = gInventory.getCategory(id); + + if (cat) + { + // Get the content + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(id,cat_array,item_array); + + // Check all the items: returns true upon encountering a nocopy item + for (LLInventoryModel::item_array_t::iterator iter = item_array->begin(); iter != item_array->end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return true; + } + } + + // Check all the sub folders recursively + for (LLInventoryModel::cat_array_t::iterator iter = cat_array->begin(); iter != cat_array->end(); iter++) + { + LLViewerInventoryCategory* cat = *iter; + if (contains_nocopy_items(cat->getUUID())) + { + return true; + } + } + } + else + { + LLInventoryItem* item = gInventory.getItem(id); + LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return true; + } + } + + // Exit without meeting a nocopy item + return false; +} + // Generates a string containing the path to the item specified by // item_id. void append_path(const LLUUID& id, std::string& path) @@ -200,15 +283,20 @@ void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistenc if (version_folder_uuid.notNull() && (!gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid) || (version_depth != 2))) { LL_INFOS("SLM") << "Unlist and clear version folder as the version folder is not at the right place anymore!!" << LL_ENDL; - LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null); + LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null,1); + } + else if (version_folder_uuid.notNull() && LLMarketplaceData::instance().getActivationState(version_folder_uuid) && (count_descendants_items(version_folder_uuid) == 0) && !LLMarketplaceData::instance().isUpdating(version_folder_uuid,version_depth)) + { + LL_INFOS("SLM") << "Unlist as the version folder is empty of any item!!" << LL_ENDL; + LLNotificationsUtil::add("AlertMerchantVersionFolderEmpty"); + LLMarketplaceData::instance().activateListing(listing_uuid, false,1); } } // Check if the count on hand needs to be updated on SLM - if (!LLMarketplaceData::instance().isUpdating(listing_uuid) && - (compute_stock_count(listing_uuid) != LLMarketplaceData::instance().getCountOnHand(listing_uuid))) + if (perform_consistency_enforcement && (compute_stock_count(listing_uuid) != LLMarketplaceData::instance().getCountOnHand(listing_uuid))) { - LLMarketplaceData::instance().updateCountOnHand(listing_uuid); + LLMarketplaceData::instance().updateCountOnHand(listing_uuid,1); } // Update all descendents starting from the listing root update_marketplace_folder_hierarchy(listing_uuid); @@ -324,7 +412,8 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, - const LLUUID& root_copy_id) + const LLUUID& root_copy_id, + bool move_no_copy_items) { // Create the initial folder LLUUID new_cat_uuid = gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName()); @@ -338,19 +427,41 @@ void copy_inventory_category(LLInventoryModel* model, LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + // If root_copy_id is null, tell the marketplace model we'll be waiting for new items to be copied over for this folder + if (root_copy_id.isNull()) + { + LLMarketplaceData::instance().setValidationWaiting(root_id,count_descendants_items(cat->getUUID())); + } + // Copy all the items 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; LLPointer 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(), - cb); + + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // If the item is nocopy, we do nothing or, optionally, move it + if (move_no_copy_items) + { + // Reparent the item + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; + gInventory.changeItemParent(viewer_inv_item, new_cat_uuid, true); + } + // Decrement the count in root_id since that one item won't be copied over + LLMarketplaceData::instance().decrementValidationWaiting(root_id); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + new_cat_uuid, + std::string(), + cb); + } } // Copy all the folders @@ -360,7 +471,7 @@ void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* category = *iter; if (category->getUUID() != root_id) { - copy_inventory_category(model, category, new_cat_uuid, root_id); + copy_inventory_category(model, category, new_cat_uuid, root_id, move_no_copy_items); } } } @@ -672,6 +783,11 @@ void open_outbox() LLFloaterOutbox::showInstance(); } +void open_marketplace_listings() +{ + LLFloaterMarketplaceListings::showInstance(); +} + // 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) @@ -900,17 +1016,23 @@ LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth) return cur_uuid; } -S32 compute_stock_count(LLUUID cat_uuid) +S32 compute_stock_count(LLUUID cat_uuid, bool force_count /* false */) { // 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; + return COMPUTE_STOCK_INFINITE; } if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) { + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + // If the folder is not completely fetched, we do not want to return any confusing value that could lead to unlisting + // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder has a count that cannot be evaluated at this time (folder not up to date) + return COMPUTE_STOCK_NOT_EVALUATED; + } // Note: stock folders are *not* supposed to have nested subfolder so we stop recursion here but we count only items (subfolders will be ignored) // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated LLInventoryModel::cat_array_t* cat_array; @@ -919,54 +1041,60 @@ S32 compute_stock_count(LLUUID cat_uuid) return item_array->size(); } - // 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)) + // When force_count is true, we do not do any verification of the marketplace status and simply compute + // the stock amount based on the descendent hierarchy. This is used specifically when creating a listing. + if (!force_count) { - // If not listed, the notion of stock is meaningless so it won't be computed for any level - return -1; - } + // 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 COMPUTE_STOCK_INFINITE; + } - LLUUID version_folder_uuid = LLMarketplaceData::instace().getVersionFolder(listing_uuid); - // Handle the case of the first 2 levels : listing and version folders - if (depth == 1) - { - if (version_folder_uuid.notNull()) + LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); + // Handle the case of the first 2 levels : listing and version folders + if (depth == 1) { - // If there is a version folder, the stock value for the listing is the version folder stock - return compute_stock_count(version_folder_uuid); + 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, true); + } + else + { + // If there's no version folder associated, the notion of stock count has no meaning + return COMPUTE_STOCK_INFINITE; + } } - else + else if (depth == 2) { - // 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; + 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 COMPUTE_STOCK_INFINITE; + } } } // In all other cases, the stock count is the min of stock folders count found in the descendents + // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder in the hierarchy has a count that cannot be evaluated at this time (folder not up to date) 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 contain any stock folders in its descendents - S32 curr_count = -1; + // "COMPUTE_STOCK_INFINITE" denotes a folder that doesn't contain any stock folders in its descendents + S32 curr_count = COMPUTE_STOCK_INFINITE; // 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))) + S32 count = compute_stock_count(category->getUUID(), true); + if ((curr_count == COMPUTE_STOCK_INFINITE) || ((count != COMPUTE_STOCK_INFINITE) && (count < curr_count))) { curr_count = count; } @@ -1128,6 +1256,11 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve // If the dest folder is a stock folder, we do not count the incoming items toward the total (stock items are seen as one) int existing_item_count = (move_in_stock ? 0 : bundle_size); + // If the dest folder is a stock folder, we do assume that the incoming items are also stock items (they should anyway) + int existing_stock_count = (move_in_stock ? bundle_size : 0); + + int existing_folder_count = 0; + // Get the version folder: that's where the counts start from const LLViewerInventoryCategory* version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); @@ -1143,6 +1276,15 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve LLInventoryModel::item_array_t existing_items; gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + existing_stock_count += count_stock_items(existing_items); + existing_folder_count += existing_categories.size(); + + // If the incoming item is a nocopy (stock) item, we need to consider that it will create a stock folder + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && !move_in_stock) + { + // Note : we do not assume that all incoming items are nocopy of different kinds... + existing_folder_count += 1; + } } if (existing_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) @@ -1153,6 +1295,22 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); accept = false; } + else if (existing_stock_count > gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); + accept = false; + } + else if (existing_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); + accept = false; + } } return accept; @@ -1193,7 +1351,9 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn int dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... int dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); + int dragged_stock_count = count_stock_items(descendent_items); int existing_item_count = 0; + int existing_stock_count = 0; int existing_folder-count = 0; if (version_folder) @@ -1203,6 +1363,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn // Clear those counts or they will be counted twice because we're already inside the version category dragged_folder_count = 0; dragged_item_count = 0; + dragged_stock_count = 0; } // Tally the total number of categories and items inside the root folder @@ -1212,10 +1373,12 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn existing_folder_count += existing_categories.size(); existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + existing_stock_count += count_stock_items(existing_items); } const int total_folder_count = existing_folder_count + dragged_folder_count; const int total_item_count = existing_item_count + dragged_item_count; + const int total_stock_count = existing_stock_count + dragged_stock_count; if (total_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) { @@ -1233,6 +1396,14 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); accept = false; } + else if (total_stock_count > gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); + accept = false; + } // Now check that each item in the folder can be moved in the marketplace if (accept && check_items) @@ -1279,6 +1450,12 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item); + // If we want to copy but the item is no copy, fail silently (this is a common case that doesn't warrant notification) + if (copy && !viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + 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. std::string error_msg; @@ -1301,7 +1478,7 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) { - // We need a stock folder + // We need to create a stock folder to move a no copy item dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName()); dest_cat = gInventory.getCategory(dest_folder); depth++; @@ -1325,7 +1502,7 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); copy_inventory_item( gAgent.getID(), - LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); + viewer_inv_item->getPermissions().getOwner(), viewer_inv_item->getUUID(), dest_folder, std::string(), @@ -1336,11 +1513,6 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol // Reparent the item gInventory.changeItemParent(viewer_inv_item, dest_folder, true); } - - // Update the modified folders - update_marketplace_category(src_folder); - update_marketplace_category(dest_folder); - gInventory.notifyObservers(); } else { @@ -1350,10 +1522,12 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol return false; } } + + open_marketplace_listings(); return true; } -bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy) +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy, bool move_no_copy_items) { // Check that we have adequate permission on all items being moved. Proceed if we do. std::string error_msg; @@ -1378,17 +1552,16 @@ bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUU if (copy) { // Copy the folder - copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder); + copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder, LLUUID::null, move_no_copy_items); } 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); } - // 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); @@ -1401,31 +1574,51 @@ bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUU LLNotificationsUtil::add("MerchantPasteFailed", subs); return false; } + + open_marketplace_listings(); return true; } +bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) +{ + return cat1->getName().compare(cat2->getName()) < 0; +} + +void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level) +{ + llinfos << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << llendl; +} + // 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 (through the // optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. // The only inventory changes that are done is to move ad sort folders containing no-copy items to stock folders. -bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) -{ - return cat1->getName().compare(cat2->getName()) < 0; -} - -bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb, bool fix_hierarchy) +bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb, bool fix_hierarchy, S32 depth) { +#if 0 + // Used only for debug + if (!cb) + { + cb = boost::bind(&dump_trace, _1, _2, _3); + } +#endif // Folder is valid unless an issues is raised bool result = true; // Get the type and the depth of the folder LLViewerInventoryCategory* viewer_cat = (LLViewerInventoryCategory*) (cat); const LLFolderType::EType folder_type = cat->getPreferredType(); - S32 depth = depth_nesting_in_marketplace(cat->getUUID()); if (depth < 0) { - // If the folder is not under the marketplace listings root, validation should not be applied - return result; + // If the depth argument was not provided, evaluate the depth directlyu + depth = depth_nesting_in_marketplace(cat->getUUID()); + } + if (depth < 0) + { + // If the folder is not under the marketplace listings root, we run validation as if it was a listing folder and prevent any hierarchy fix + // This allows the function to be used to pre-validate a folder anywhere in the inventory + depth = 1; + fix_hierarchy = false; } // Set the indentation for print output (typically, audit button in marketplace folder floater) @@ -1446,7 +1639,7 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb) { message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; - cb(message,LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } } } @@ -1459,14 +1652,14 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } // Nest the stock folder one level deeper in a normal folder ad restart from there LLUUID parent_uuid = cat->getParentUUID(); LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); - result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy); + result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1); return result; } else @@ -1475,25 +1668,25 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb(message, LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } } } + // Item sorting and validation : sorting and moving the various stock items is complicated as the set of constraints is high + // 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 + 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 + // We use a composite (type,permission) key on that map to store UUIDs of items of same (type,permissions) + std::map > items_vector; - std::vector > 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 + // Parse the items and create vectors of item UUIDs sorting copyable items and stock items of various types bool has_bad_items = false; 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++) @@ -1509,44 +1702,41 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb && fix_hierarchy) { std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb(message, LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } continue; } // Update the appropriate vector item for that type LLInventoryType::EType type = LLInventoryType::IT_COUNT; // Default value for non stock items + U32 perms = 0; if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { // Get the item type for stock items type = viewer_inv_item->getInventoryType(); + perms = viewer_inv_item->getPermissions().getMaskNextOwner(); } - items_vector[type].push_back(viewer_inv_item); + U32 key = (((U32)(type) & 0xFF) << 24) | (perms & 0xFFFFFF); + items_vector[key].push_back(viewer_inv_item->getUUID()); } - // 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); - } - } + // How many types of items? Which type is it if only one? + S32 count = items_vector.size(); + U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24; // This is the key for any normal copyable item + U32 unique_key = (count == 1 ? items_vector.begin()->first : default_key); // The key in the case of one item type only // If we have no items in there (only folders or empty), analyze a bit further if ((count == 0) && !has_bad_items) { if (cat_array->size() == 0) { + // So we have no item and no folder. That's at least a warning. if (depth == 2) { // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) if (cb) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } } else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) @@ -1555,14 +1745,14 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } } else if (cb) { // We warn if there's nothing in a regular folder (maybe it's an under construction listing) std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } } else @@ -1571,34 +1761,43 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb && result && (depth >= 1)) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message, LLError::LEVEL_INFO); + cb(message, depth, LLError::LEVEL_INFO); } } } // If we have a single type of items of the right type in the right place, we're done - else if ((count == 1) && !has_bad_items && (((type == LLInventoryType::IT_COUNT) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) + else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) { - // Done with that folder : PRint out the folder name unless we alreaady found an error here + // Done with that folder : Print out the folder name unless we alreaady found an error here if (cb && result && (depth >= 1)) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message, LLError::LEVEL_INFO); + cb(message, depth, LLError::LEVEL_INFO); } } else { if (fix_hierarchy && !has_bad_items) { - // Create one folder per vector at the right depth and of the right type - for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++) + // Alert the user when an existing stock folder has to be split + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && ((count >= 2) || (cat_array->size() > 0))) { - if (!items_vectory[i].empty()) + LLNotificationsUtil::add("AlertMerchantStockFolderSplit"); + } + // If we have more than 1 type of items or we are at the listing level or we have stock/no stock type mismatch, wrap the items in subfolders + if ((count > 1) || (depth == 1) || + ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key == default_key)) || + ((folder_type != LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key != default_key))) + { + // Create one folder per vector at the right depth and of the right type + std::map >::iterator items_vector_it = items_vector.begin(); + while (items_vector_it != items_vector.end()) { // Create a new folder LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); - LLViewerInventoryItem* viewer_inv_item = items_vector[i].back(); - std::string folder_name = (depth > 1 ? viewer_cat->getName() : viewer_inv_item->getName()); - LLFolderType::EType new_folder_type = (i == LLInventoryType::IT_COUNT ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); + std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); + LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); if (cb) { std::string message = ""; @@ -1610,35 +1809,41 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ { message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); } - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name); + // Move each item to the new folder - while (!items_vector[i].empty()) + while (!items_vector_it->second.empty()) { - LLViewerInventoryItem* viewer_inv_item = items_vector[i].back(); + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); if (cb) { std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } gInventory.changeItemParent(viewer_inv_item, folder_uuid, true); - items_vector[i].pop_back(); + items_vector_it->second.pop_back(); } + + // Next type + update_marketplace_category(parent_uuid); update_marketplace_category(folder_uuid); gInventory.notifyObservers(); + items_vector_it++; } } // Stock folder should have no sub folder so reparent those up if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK) { LLUUID parent_uuid = cat->getParentUUID(); + gInventory.getDirectDescendentsOf(cat->getUUID(),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++) { LLViewerInventoryCategory* viewer_cat = (LLViewerInventoryCategory*) (*iter); gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); - // Note : those reparented folders will be recursively visited and validated at the end of this function + result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth); } } } @@ -1653,20 +1858,20 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ // Report if a stock folder contains a mix of items result = false; std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); - cb(message, LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) { // Report if a stock folder contains subfolders result = false; std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); - cb(message, LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } else { // Simply print the folder name std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message, LLError::LEVEL_INFO); + cb(message, depth, LLError::LEVEL_INFO); } } @@ -1682,20 +1887,21 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ // Report items that shouldn't be there to start with result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb(message, LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) { // Report stock items that are misplaced result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); - cb(message, LLError::LEVEL_ERROR); + cb(message, depth, LLError::LEVEL_ERROR); } else if (depth == 1) { // Report items not wrapped in version folder + result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_ERROR); } } } @@ -1707,21 +1913,16 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ if (cb) { std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); - cb(message, LLError::LEVEL_WARN); + cb(message, depth, LLError::LEVEL_WARN); } gInventory.removeCategory(cat->getUUID()); gInventory.notifyObservers(); return result && !has_bad_items; } - else - { - // Update the current folder - update_marketplace_category(cat->getUUID()); - gInventory.notifyObservers(); - } } // Recursion : Perform the same validation on each nested folder + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); LLInventoryModel::cat_array_t cat_array_copy = *cat_array; // Sort the folders in alphabetical order first std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha); @@ -1729,9 +1930,11 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { LLInventoryCategory* category = *iter; - result &= validate_marketplacelistings(category, cb, fix_hierarchy); + result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1); } + update_marketplace_category(cat->getUUID()); + gInventory.notifyObservers(); return result && !has_bad_items; } diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 230980960..11382b48c 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -32,6 +32,10 @@ #include "llinventory.h" #include "llwearabletype.h" +// compute_stock_count() return error code +const S32 COMPUTE_STOCK_INFINITE = -1; +const S32 COMPUTE_STOCK_NOT_EVALUATED = -2; + /******************************************************************************** ** ** ** MISCELLANEOUS GLOBAL FUNCTIONS @@ -62,7 +66,7 @@ void update_all_marketplace_count(); 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); +void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false); // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); @@ -74,16 +78,16 @@ void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LL 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 validation_callback_t; +typedef boost::function validation_callback_t; bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false); bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false); bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false); -bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false); -bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true); +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false); +bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1); S32 depth_nesting_in_marketplace(LLUUID cur_uuid); LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); -S32 computer_stock_count(LLUUID cat_uuid); +S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); /** Miscellaneous global functions ** ** diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index b8b3d6c8d..d3e45df42 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -38,6 +38,7 @@ #include "llinventoryobserver.h" #include "llinventorypanel.h" #include "llnotificationsutil.h" +#include "llmarketplacefunctions.h" #include "llwindow.h" #include "llviewercontrol.h" #include "llpreview.h" @@ -1161,7 +1162,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id)) { - mask |= LLInventoryObeserver::LABEL; + mask |= LLInventoryObserver::LABEL; } old_cat->copyViewerCategory(cat); addChangedMask(mask, cat->getUUID()); @@ -2093,13 +2094,18 @@ bool LLInventoryModel::loadSkeleton( { continue; } - if (cat->getVersion() != tcat->getVersion()) + else if (cat->getVersion() != tcat->getVersion()) { // if the cached version does not match the server version, // throw away the version we have so we can fetch the // correct contents the next time the viewer opens the folder. tcat->setVersion(NO_VERSION); } + else if (tcat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // Do not trust stock folders being updated + tcat->setVersion(NO_VERSION); + } else { cached_ids.insert(tcat->getUUID()); @@ -3090,7 +3096,18 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' (" << tfolder->getUUID() << ") in " << tfolder->getParentUUID() << LL_ENDL; - if(tfolder->getUUID().notNull()) + + // If the folder is a listing or a version folder, all we need to do is update the SLM data + int depth_folder = depth_nesting_in_marketplace(tfolder->getUUID()); + if ((depth_folder == 1) || (depth_folder == 2)) + { + // Trigger an SLM listing update + LLUUID listing_uuid = (depth_folder == 1 ? tfolder->getUUID() : tfolder->getParentUUID()); + S32 listing_id = LLMarketplaceData::instance().getListingID(listing_uuid); + LLMarketplaceData::instance().getListing(listing_id); + // In that case, there is no item to update so no callback -> we skip the rest of the update + } + else if(tfolder->getUUID().notNull()) { folders.push_back(tfolder); LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index d6ccd56e2..ec21e4663 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -37,6 +37,7 @@ #include "llfolderview.h" #include "llimview.h" #include "llinventorybridge.h" +#include "llinventoryfilter.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" #include "llviewerfoldertype.h" @@ -80,12 +81,6 @@ protected: LLInventoryPanel* mIP; }; - - - - - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInvPanelComplObserver // @@ -129,6 +124,10 @@ void LLInvPanelComplObserver::done() mCallback(); } +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryPanel +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LLInventoryPanel::LLInventoryPanel(const std::string& name, const std::string& sort_order_setting, const std::string& start_folder, @@ -142,6 +141,10 @@ LLInventoryPanel::LLInventoryPanel(const std::string& name, mScroller(NULL), mSortOrderSetting(sort_order_setting), mStartFolder(start_folder), + mShowRootFolder(false), + mAllowDropOnRoot(true), + mAllowOpen(true), + mUseMarketplaceFolders(false), mInventory(inventory), mAllowMultiSelect(allow_multi_select), mViewsInitialized(false), @@ -183,7 +186,7 @@ void LLInventoryPanel::buildFolderView() } LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, + (mUseMarketplaceFolders/*mParams.use_marketplace_folders*/ ? LLAssetType::AT_MARKETPLACE_FOLDER : LLAssetType::AT_CATEGORY), LLInventoryType::IT_CATEGORY, this, NULL, @@ -191,6 +194,8 @@ void LLInventoryPanel::buildFolderView() LLFolderView* folder_view = createFolderView(new_listener, true/*params.use_label_suffix()*/); mFolderRoot = folder_view->getHandle(); + + addItemID(root_id, mFolderRoot.get()); } BOOL LLInventoryPanel::postBuild() { @@ -223,6 +228,7 @@ BOOL LLInventoryPanel::postBuild() { initializeViews(); } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -240,9 +246,8 @@ BOOL LLInventoryPanel::postBuild() //getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); // Singu Nope! getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); } - // hide marketplace listing box, unless we want to show it (note: hacky as show_root_folder is only used for marketplace...) - // Singu Note: Even more hacky! String compare for merchant StartFolder to create the same effect /*&& !mParams.show_root_folder*/ - if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && mStartFolder != "merchant") + // hide marketplace listing box, unless we are a marketplace panel + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mUseMarketplaceFolders/*mParams.use_marketplace_folders*/) { getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); } @@ -250,7 +255,7 @@ BOOL LLInventoryPanel::postBuild() // set the filter for the empty folder if the debug setting is on if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) { - getFilter()->setFilterEmptySystemFolders(); + getFilter().setFilterEmptySystemFolders(); } return LLPanel::postBuild(); @@ -260,7 +265,7 @@ LLInventoryPanel::~LLInventoryPanel() { if (mFolderRoot.get()) { - U32 sort_order = mFolderRoot.get()->getSortOrder(); + U32 sort_order = mFolderRoot.get().getSortOrder(); if (mSortOrderSetting != INHERIT_SORT_ORDER) { gSavedSettings.setU32(mSortOrderSetting, sort_order); @@ -353,6 +358,12 @@ LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac rect, &gInventory, allow_multi_select, parent); + // Singu TODO: Turn these into mParams like upstream. + node->getAttribute_bool("show_root_folder", panel->mShowRootFolder); + node->getAttribute_bool("allow_drop_on_root", panel->mAllowDropOnRoot); + node->getAttribute_bool("allow_open", panel->mAllowOpen); + node->getAttribute_bool("use_marketplace_folders", panel->mUseMarketplaceFolders); + panel->initFromXML(node, parent); panel->postBuild(); @@ -364,33 +375,26 @@ void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) mFolderRoot.get()->updateSelection(); + LLPanel::draw(); } -LLInventoryFilter* LLInventoryPanel::getFilter() +LLInventoryFilter& LLInventoryPanel::getFilter() { - if (mFolderRoot.get()) - { - return mFolderRoot.get()->getFilter(); - } - return NULL; + return *mFolderRoot.get()->getFilter(); } -const LLInventoryFilter* LLInventoryPanel::getFilter() const +const LLInventoryFilter& LLInventoryPanel::getFilter() const { - if (mFolderRoot.get()) - { - return mFolderRoot.get()->getFilter(); - } - return NULL; + return *mFolderRoot.get()->getFilter(); } void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type) { if (filter_type == LLInventoryFilter::FILTERTYPE_OBJECT) - getFilter()->setFilterObjectTypes(types); + getFilter().setFilterObjectTypes(types); if (filter_type == LLInventoryFilter::FILTERTYPE_CATEGORY) - getFilter()->setFilterCategoryTypes(types); + getFilter().setFilterCategoryTypes(types); } U32 LLInventoryPanel::getFilterObjectTypes() const @@ -406,17 +410,17 @@ U32 LLInventoryPanel::getFilterPermMask() const void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) { - getFilter()->setFilterPermissions(filter_perm_mask); + getFilter().setFilterPermissions(filter_perm_mask); } void LLInventoryPanel::setFilterWearableTypes(U64 types) { - getFilter()->setFilterWearableTypes(types); + getFilter().setFilterWearableTypes(types); } void LLInventoryPanel::setFilterWorn(bool worn) { - getFilter()->setFilterWorn(worn); + getFilter().setFilterWorn(worn); } void LLInventoryPanel::setFilterSubString(const std::string& string) @@ -451,7 +455,7 @@ void LLInventoryPanel::setFilterSubString(const std::string& string) getRootFolder()->saveFolderState(); } // set new filter string - getFilter()->setFilterSubString(string); + getFilter().setFilterSubString(string); } } @@ -462,8 +466,8 @@ const std::string LLInventoryPanel::getFilterSubString() void LLInventoryPanel::setSortOrder(U32 order) { - getFilter()->setSortOrder(order); - if (getFilter()->isModified()) + getFilter().setSortOrder(order); + if (getFilter().isModified()) { mFolderRoot.get()->setSortOrder(order); // try to keep selection onscreen, even if it wasn't to start with @@ -483,27 +487,27 @@ void LLInventoryPanel::requestSort() void LLInventoryPanel::setSinceLogoff(BOOL sl) { - getFilter()->setDateRangeLastLogoff(sl); + getFilter().setDateRangeLastLogoff(sl); } void LLInventoryPanel::setHoursAgo(U32 hours) { - getFilter()->setHoursAgo(hours); + getFilter().setHoursAgo(hours); } void LLInventoryPanel::setFilterLinks(U64 filter_links) { - getFilter()->setFilterLinks(filter_links); + getFilter().setFilterLinks(filter_links); } void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) { - getFilter()->setShowFolderState(show); + getFilter().setShowFolderState(show); } LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() { - return getFilter()->getShowFolderState(); + return getFilter().getShowFolderState(); } void LLInventoryPanel::modelChanged(U32 mask) @@ -569,7 +573,9 @@ void LLInventoryPanel::modelChanged(U32 mask) { if (model_item && view_item) { + const LLUUID& idp = view_item->getListener()->getUUID(); view_item->destroyView(); + removeItemID(idp); } view_item = buildNewViews(item_id); view_folder = dynamic_cast(view_item); @@ -637,6 +643,7 @@ void LLInventoryPanel::modelChanged(U32 mask) // Item is to be moved and we found its new parent in the panel's directory, so move the item's UI. view_item->getParentFolder()->extractItem(view_item); view_item->addToFolder(new_parent, mFolderRoot.get()); + addItemID(view_item->getListener()->getUUID(), view_item); if (mInventory) { const LLUUID trash_id = mInventory->findCategoryUUIDForType(LLFolderType::FT_TRASH); @@ -648,6 +655,10 @@ void LLInventoryPanel::modelChanged(U32 mask) } else { + // Remove the item ID before destroying the view because the view-model-item gets + // destroyed when the view is destroyed + removeItemID(view_item->getListener()->getUUID()); + // Item is to be moved outside the panel's directory (e.g. moved to trash for a panel that // doesn't include trash). Just remove the item's UI. view_item->destroyView(); @@ -662,12 +673,33 @@ void LLInventoryPanel::modelChanged(U32 mask) else if (!model_item && view_item) { // Remove the item's UI. + LLFolderViewFolder* parent = view_item->getParentFolder(); + removeItemID(view_item->getListener()->getUUID()); view_item->destroyView(); } } } } +const LLUUID LLInventoryPanel::getRootFolderID() const +{ + LLUUID root_id; + if (mFolderRoot.get() && mFolderRoot.get()->getListener()) + { + root_id = mFolderRoot.get()->getListener()->getUUID(); + } + else + { + root_id = getStartFolder(mStartFolder); + if (root_id.isNull()) + { + LL_WARNS() << "Could not find folder of type " << mStartFolder << LL_ENDL; + root_id.generateNewID(); + } + } + return root_id; +} + // static void LLInventoryPanel::onIdle(void *userdata) { @@ -686,24 +718,7 @@ void LLInventoryPanel::onIdle(void *userdata) } } -const LLUUID& LLInventoryPanel::getRootFolderID() const -{ - LLUUID root_id; - if (mFolderRoot.get() && mFolderRoot.get()->getListener()) - { - root_id = mFolderRoot.get()->getListener()->getUUID(); - } - else - { - root_id = getStartFolder(mStartFolder); - if (root_id.isNull()) - { - llwarns << "Could not find folder of type " << preferred_type << LL_ENDL; - root_id.generateNewID(); - } - } - return root_id; -} + void LLInventoryPanel::initializeViews() { @@ -799,28 +814,22 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) const LLUUID& parent_id = objectp->getParentUUID(); LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)mFolderRoot.get()->getItemByID(parent_id); + LLUUID root_id = getRootFolderID(); // Force the creation of an extra root level folder item if required by the inventory panel (default is "false") bool allow_drop = true; - bool allow_wear = true; // Singu TODO: Remove this when we have params here. bool create_root = false; - if (mStartFolder == "merchant") // Singu Note: Only done for "merchant" + if (mShowRootFolder) { - LLUUID root_id = getRootFolderID(); if (root_id == id) { // We insert an extra level that's seen by the UI but has no influence on the model parent_folder = dynamic_cast(folder_view_item); folder_view_item = NULL; - allow_drop = false; // merchant floaters upstream say false in xui - allow_wear = false; // merchant floaters upstream say false in xui + allow_drop = mAllowDropOnRoot; create_root = true; } } - else if (mStartFolder == "outbox") - { - allow_wear = false; // outbox floater upstream says false in xui - } if (!folder_view_item && parent_folder) { @@ -844,12 +853,20 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) objectp->getUUID()); if (new_listener) { + // Singu Note: new_listener disappeared in the last merge, be sure that adding it back here doesn't break anything. ~Liru + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), + objectp->getType(), + LLInventoryType::IT_CATEGORY, + this, + mFolderRoot.get(), + objectp->getUUID()); + LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); if (folderp) { folderp->setItemSortOrder(mFolderRoot.get()->getSortOrder()); folderp->setAllowDrop(allow_drop); - folderp->setAllowWear(allow_wear); + folderp->setAllowOpen(mAllowOpen); } folder_view_item = folderp; } @@ -876,6 +893,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { llassert(parent_folder != NULL); folder_view_item->addToFolder(parent_folder, mFolderRoot.get()); + addItemID(id, folder_view_item); // In the case of the root folder been shown, open that folder by default once the widget is created if (create_root) { @@ -938,24 +956,6 @@ void LLInventoryPanel::openStartFolderOrMyInventory() } } -struct LLConfirmPurgeData -{ - LLUUID mID; - LLInventoryModel* mModel; -}; - -class LLIsNotWorn : public LLInventoryCollectFunctor -{ -public: - LLIsNotWorn() {} - virtual ~LLIsNotWorn() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item) - { - return !gAgentWearables.isWearingItem(item->getUUID()); - } -}; - class LLOpenFolderByID : public LLFolderViewFunctor { public: @@ -1023,7 +1023,7 @@ BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // If folder view is empty the (x, y) point won't be in its rect // so the handler must be called explicitly. // but only if was not handled before. See EXT-6746. - if (!handled && /*mParams.allow_drop_on_root*/mStartFolder != "merchant" && !mFolderRoot.get()->hasVisibleChildren()) + if (!handled && /*mParams.allow_drop_on_root*/mAllowDropOnRoot && !mFolderRoot.get()->hasVisibleChildren()) { handled = mFolderRoot.get()->handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } @@ -1097,6 +1097,7 @@ void LLInventoryPanel::setSelectCallback(const boost::functionclearSelection(); + mSelectThisID.setNull(); } void LLInventoryPanel::onSelectionChange(const std::deque& items, BOOL user_action) @@ -1155,7 +1156,7 @@ void LLInventoryPanel::createNewItem(const std::string& name, BOOL LLInventoryPanel::getSinceLogoff() { - return getFilter()->isSinceLogoff(); + return getFilter().isSinceLogoff(); } // DEBUG ONLY @@ -1190,18 +1191,170 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) res = floater_inventory->getActivePanel(); } + return res; } void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type) { - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << folder_type)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type)); } BOOL LLInventoryPanel::getIsHiddenFolderType(LLFolderType::EType folder_type) const { - return !(getFilter()->getFilterCategoryTypes() & (1ULL << folder_type)); + return !(getFilter().getFilterCategoryTypes() & (1ULL << folder_type)); } +void LLInventoryPanel::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLInventoryPanel::removeItemID(const LLUUID& id) +{ + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(id, categories, items, TRUE); + + mItemMap.erase(id); + + for (LLInventoryModel::cat_array_t::iterator it = categories.begin(), end_it = categories.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); +} + + for (LLInventoryModel::item_array_t::iterator it = items.begin(), end_it = items.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } +} + +static LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); +LLFolderViewItem* LLInventoryPanel::getItemByID(const LLUUID& id) +{ + LLFastTimer mew(FTM_GET_ITEM_BY_ID); + + std::map::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id) +{ + LLFolderViewItem* item = getItemByID(id); + return dynamic_cast(item); +} + +void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL take_keyboard_focus ) +{ + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getListener()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else + { + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; + } +} + +void LLInventoryPanel::updateSelection() +{ + if (mSelectThisID.notNull()) + { + setSelectionByID(mSelectThisID, false); + } +} + +namespace LLInventoryAction +{ + bool doToSelected(LLFolderView* root, std::string action); +} + +void LLInventoryPanel::doToSelected(const LLSD& userdata) +{ + LLInventoryAction::doToSelected(mFolderRoot.get(), userdata.asString()); + + return; +} + +BOOL LLInventoryPanel::handleKeyHere( KEY key, MASK mask ) +{ + BOOL handled = FALSE; + switch (key) + { + case KEY_RETURN: + // Open selected items if enter key hit on the inventory panel + if (mask == MASK_NONE) + { + //Don't allow attaching or opening items from Merchant Outbox + LLFolderViewItem* folder_item = mFolderRoot.get()->getCurSelectedItem(); + if(folder_item) + { + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); + if(bridge && bridge->isOutboxFolder() && (bridge->getInventoryType() != LLInventoryType::IT_CATEGORY)) + { + return handled; + } + } + + LLInventoryAction::doToSelected(mFolderRoot.get(), "open"); + handled = TRUE; + } + break; + case KEY_DELETE: + case KEY_BACKSPACE: + // Delete selected items if delete or backspace key hit on the inventory panel + // Note: on Mac laptop keyboards, backspace and delete are one and the same + if (isSelectionRemovable() && (mask == MASK_NONE)) + { + LLInventoryAction::doToSelected(mFolderRoot.get(), "delete"); + handled = TRUE; + } + break; + } + return handled; +} + +bool LLInventoryPanel::isSelectionRemovable() +{ + bool can_delete = false; + if (mFolderRoot.get()) + { + std::set selection_set = mFolderRoot.get()->getSelectionList(); + if (!selection_set.empty()) + { + can_delete = true; + for (std::set::iterator iter = selection_set.begin(); + iter != selection_set.end(); + ++iter) + { + LLFolderViewItem *item = getItemByID(*iter); + const LLFolderViewEventListener* listener =item->getListener(); + if (!listener) + { + can_delete = false; + } + else + { + can_delete &= listener->isItemRemovable() && !listener->isItemInTrash(); + } + } + } + } + return can_delete; +} /************************************************************************/ /* Recent Inventory Panel related class */ @@ -1219,6 +1372,7 @@ LLInventoryRecentItemsPanel:: LLInventoryRecentItemsPanel(const std::string& nam LLView *parent_view) : LLInventoryPanel(name, sort_order_setting, start_folder,rect,inventory,allow_multi_select,parent_view) { + // replace bridge builder to have necessary View bridges. mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; } diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 63893c4f3..8f571e075 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -1,6 +1,6 @@ /** - * @file llpanelmaininventory.h - * @brief LLPanelMainInventory, LLInventoryFolder, and LLInventoryItem + * @file llinventorypanel.h + * @brief LLInventoryPanel * class definition * * $LicenseInfo:firstyear=2001&license=viewergpl$ @@ -87,6 +87,7 @@ public: // LLView methods void draw(); + /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); BOOL handleHover(S32 x, S32 y, MASK mask); BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -104,8 +105,9 @@ public: void setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus); void setSelectCallback(const boost::function& items, BOOL user_action)>& cb); void clearSelection(); - LLInventoryFilter* getFilter(); - const LLInventoryFilter* getFilter() const; + bool isSelectionRemovable(); + LLInventoryFilter& getFilter(); + const LLInventoryFilter& getFilter() const; void setFilterTypes(U64 filter, LLInventoryFilter::EFilterType = LLInventoryFilter::FILTERTYPE_OBJECT); U32 getFilterObjectTypes() const; void setFilterPermMask(PermissionMask filter_perm_mask); @@ -115,7 +117,7 @@ public: const std::string getFilterSubString(); void setFilterWorn(bool worn); bool getFilterWorn() const { return mFolderRoot.get()->getFilterWorn(); } - + void setSinceLogoff(BOOL sl); void setHoursAgo(U32 hours); BOOL getSinceLogoff(); @@ -128,11 +130,14 @@ public: void modelChanged(U32 mask); LLFolderView* getRootFolder() { return mFolderRoot.get(); } LLScrollContainer* getScrollableContainer() { return mScroller; } - bool getAllowDropOnRoot() const { return mStartFolder != "merchant"; } + bool getAllowDropOnRoot() const { return mAllowDropOnRoot; } void onSelectionChange(const std::deque &items, BOOL user_action); LLHandle getInventoryPanelHandle() const { return getDerivedHandle(); } + + // Callbacks + void doToSelected(const LLSD& userdata); // DEBUG ONLY: static void dumpSelectionInformation(void* user_data); @@ -156,6 +161,13 @@ public: LLInventoryType::EType inv_type, U32 next_owner_perm = 0); + void addItemID(const LLUUID& id, LLFolderViewItem* itemp); + void removeItemID(const LLUUID& id); + LLFolderViewItem* getItemByID(const LLUUID& id); + LLFolderViewFolder* getFolderByID(const LLUUID& id); + void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); + void updateSelection(); + // Clean up stuff when the folder root gets deleted void clearFolderRoot(); @@ -163,15 +175,17 @@ protected: void openStartFolderOrMyInventory(); // open the first level of inventory void onItemsCompletion(); // called when selected items are complete + LLUUID mSelectThisID; LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; LLInvPanelComplObserver* mCompletionObserver; - - BOOL mAllowMultiSelect; + bool mAllowMultiSelect; + LLHandle mFolderRoot; LLScrollContainer* mScroller; LLPointer mGroupedItemBridge; + std::map mItemMap; /** * Pointer to LLInventoryFolderViewModelBuilder. * @@ -197,6 +211,10 @@ public: private: const std::string mStartFolder; + bool mShowRootFolder; + bool mAllowDropOnRoot; + bool mAllowOpen; + bool mUseMarketplaceFolders; const std::string mSortOrderSetting; //-------------------------------------------------------------------- @@ -207,7 +225,7 @@ public: public: BOOL getIsViewsInitialized() const { return mViewsInitialized; } - const LLUUID& getRootFolderID() const; + const LLUUID getRootFolderID() const; protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(); @@ -225,9 +243,6 @@ protected: class LLPanelMainInventory; - - - ///---------------------------------------------------------------------------- /// Function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -250,6 +265,3 @@ const BOOL TAKE_FOCUS_YES = TRUE; const BOOL TAKE_FOCUS_NO = FALSE; #endif // LL_LLINVENTORYPANEL_H - - - diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 93dd2fbe4..a2aa3ce01 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -43,8 +43,8 @@ #include "llviewerregion.h" #include "hippogridmanager.h" -#include "reader.h" // JSON -#include "writer.h" // JSON +#include "json/reader.h" // JSON +#include "json/writer.h" // JSON // // Helpers @@ -129,17 +129,43 @@ LLSD getMarketplaceStringSubstitutions() return marketplace_sub_map; } +// Get the version folder: if there is only one subfolder, we will use it as a version folder +LLUUID getVersionFolderIfUnique(const LLUUID& folder_id) +{ + LLUUID version_id = LLUUID::null; + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, categories, items); + if (categories->size() == 1) + { + version_id = categories->begin()->get()->getUUID(); + } + else + { + LLNotificationsUtil::add("AlertMerchantListingActivateRequired"); + } + return version_id; +} + /////////////////////////////////////////////////////////////////////////////// // SLM Responders -void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string description) +void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description) { LL_WARNS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", reason : " << reason << ", code : " << code << ", description : " << description << LL_ENDL; - // Prompt the user with the warning (so they know why things are failing) - LLSD subs; - subs["[ERROR_REASON]"] = reason; - // We do show long descriptions in the alert (unlikely to be readable). The description string will be in the log though. - subs["[ERROR_DESCRIPTION]"] = (description.length() <= 512 ? description : ""); - LLNotificationsUtil::add("MerchantTransactionFailed", subs); + if ((status == 422) && (description == "[\"You must have an English description to list the product\", \"You must choose a category for your product before it can be listed\", \"Listing could not change state.\", \"Price can't be blank\"]")) + { + // Unprocessable Entity : Special case that error as it is a frequent answer when trying to list an incomplete listing + LLNotificationsUtil::add("MerchantUnprocessableEntity"); + } + else + { + // Prompt the user with the warning (so they know why things are failing) + LLSD subs; + subs["[ERROR_REASON]"] = reason; + // We do show long descriptions in the alert (unlikely to be readable). The description string will be in the log though. + subs["[ERROR_DESCRIPTION]"] = (description.length() <= 512 ? description : ""); + LLNotificationsUtil::add("MerchantTransactionFailed", subs); + } } void log_SLM_infos(const std::string& request, U32 status, const std::string& body) { @@ -148,10 +174,13 @@ void log_SLM_infos(const std::string& request, U32 status, const std::string& bo LL_INFOS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", body or description : " << body << LL_ENDL; } } - -// Merov: This is a temporary hack used by dev while secondlife-staging is down... -// *TODO : Suppress that before shipping! -static bool sBypassMerchant = false; +void log_SLM_infos(const std::string& request, const std::string& url, const std::string& body) +{ + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + LL_INFOS("SLM") << "SLM API : Sending " << request << ". url : " << url << ", body : " << body << LL_ENDL; + } +} class LLSLMGetMerchantResponder : public LLHTTPClient::ResponderWithResult { @@ -163,13 +192,7 @@ public: protected: virtual void httpFailure() { - if (sBypassMerchant) - { - // *TODO : Suppress that before shipping! - log_SLM_infos("Get /merchant", getStatus(), "SLM Connection error bypassed (debug only)"); - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT); - } - else if (HTTP_NOT_FOUND == getStatus()) + if (HTTP_NOT_FOUND == getStatus()) { log_SLM_infos("Get /merchant", getStatus(), "User is not a merchant"); LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); @@ -213,11 +236,12 @@ public: LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); - const std:string body = strstrm.str(); + const std::string body = strstrm.str(); - if (!isGoodStatus()) + if (!isGoodStatus(mStatus)) { log_SLM_warning("Get /listings", getStatus(), getReason(), "", body); + LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED); update_marketplace_category(mExpectedFolderId, false); gInventory.notifyObservers(); return; @@ -228,6 +252,7 @@ public: if (!reader.parse(body, root)) { log_SLM_warning("Get /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body); + LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED); update_marketplace_category(mExpectedFolderId, false); gInventory.notifyObservers(); return; @@ -253,17 +278,13 @@ public: LLUUID version_id(version_uuid_string); if (folder_id.notNull()) { - LLMarketplaceData::instance().deleteListing(folder_id, false); - LLMarketplaceData::instance().addListing(folder_id, listing_id, version_id, is_listed, false); - LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); - LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); - update_marketplace_category(folder_id, false); - gInventory.notifyObservers(); + LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); } it++; } // Update all folders under the root + LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_DONE); update_marketplace_category(mExpectedFolderId, false); gInventory.notifyObservers(); } @@ -280,7 +301,7 @@ public: LLSLMCreateListingsResponder(const LLUUID& folder_id) { - mExpectedFolderId = folder_id + mExpectedFolderId = folder_id; } virtual void completedRaw(const LLChannelDescriptors& channels, @@ -291,9 +312,9 @@ public: LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); - const std::String body = strstrm.str(); + const std::string body = strstrm.str(); - if (!isGoodStatus()) + if (!isGoodStatus(mStatus)) { log_SLM_warning("Post /listings", getStatus(), getReason(), "", body); update_marketplace_category(mExpectedFolderId, false); @@ -329,9 +350,7 @@ public: LLUUID folder_id(folder_uuid_string); LLUUID version_id(version_uuid_string); - LLMarketplaceData::instance().addListing(folder_id, listing_id, version_id, is_listed, false); - LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); - LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); update_marketplace_category(folder_id, false); gInventory.notifyObservers(); it++; @@ -361,11 +380,19 @@ public: LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); - const std:string body = strstrm.str(); + const std::string body = strstrm.str(); - if (!isGoodStatus()) + if (!isGoodStatus(mStatus)) { - log_SLM_warning("Get /listing", getStatus(), getReason(), "", body); + if (getStatus() == 404) + { + // That listing does not exist -> delete its record from the local SLM data store + LLMarketplaceData::instance().deleteListing(mExpectedFolderId, false); + } + else + { + log_SLM_warning("Get /listing", getStatus(), getReason(), "", body); + } update_marketplace_category(mExpectedFolderId, false); gInventory.notifyObservers(); return; @@ -438,9 +465,9 @@ public: LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); - const std:string body = strstrm.str(); + const std::string body = strstrm.str(); - if (!isGoodStatus()) + if (!isGoodStatus(mStatus)) { log_SLM_warning("Put /listing", getStatus(), getReason(), "", body); update_marketplace_category(mExpectedFolderId, false); @@ -526,9 +553,9 @@ public: LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); - const std:string body = strstrm.str(); + const std::string body = strstrm.str(); - if (!isGoodStatus()) + if (!isGoodStatus(mStatus)) { log_SLM_warning("Put /associate_inventory", getStatus(), getReason(), "", body); update_marketplace_category(mExpectedFolderId, false); @@ -576,14 +603,13 @@ public: } // Add the new association - LLMarketplaceData::instance().addListing(folder_id, listing_id, version_id, is_listed, false); - LLMarketplaceData::instance().setListingURL(folder_id, edit_url, false); - LLMarketplaceData::instance().setCountOnHand(folder_id, count, false); + LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); update_marketplace_category(folder_id, false); gInventory.notifyObservers(); - // Alert with DAMA informing the user that a version folder must be designated - LLNotificationsUtil::add("AlertMerchantAssociateNeedsVersion"); + // the stock count needs to be updated with the new local count now + LLMarketplaceData::instance().updateCountOnHand(folder_id,1); + it++; } @@ -615,9 +641,9 @@ public: LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); - const std:string body = strstrm.str(); + const std::string body = strstrm.str(); - if (!isGoodStatus()) + if (!isGoodStatus(mStatus)) { log_SLM_warning("Delete /listing", getStatus(), getReason(), "", body); update_marketplace_category(mExpectedFolderId, false); @@ -1110,13 +1136,43 @@ public: void LLMarketplaceInventoryObserver::changed(U32 mask) { + // When things are added to the marketplace, we might need to re-validate and fix the containing listings + if (mask & LLInventoryObserver::ADD) + { + const std::set& changed_items = gInventory.getChangedIDs(); + + std::set::const_iterator id_it = changed_items.begin(); + std::set::const_iterator id_end = changed_items.end(); + // First, count the number of items in this list... + S32 count = 0; + for (;id_it != id_end; ++id_it) + { + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj && (LLAssetType::AT_CATEGORY != obj->getType())) + { + count++; + } + } + // Then, decrement the folders of that amount + // Note that of all of those, only one folder will be a listing folder (if at all). + // The other will be ignored by the decrement method. + id_it = changed_items.begin(); + for (;id_it != id_end; ++id_it) + { + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj && (LLAssetType::AT_CATEGORY == obj->getType())) + { + LLMarketplaceData::instance().decrementValidationWaiting(obj->getUUID(),count); + } + } + } + // When things are changed in the inventory, this can trigger a host of changes in the marketplace listings folder: // * stock counts changing : no copy items coming in and out will change the stock count on folders // * version and listing folders : moving those might invalidate the marketplace data itself // Since we should cannot raise inventory change while the observer is called (the list will be cleared // once observers are called) we need to raise a flag in the inventory to signal that things have been dirtied. - // That's the only changes that really do make sense for marketplace to worry about if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE)) { const std::set& changed_items = gInventory.getChangedIDs(); @@ -1184,7 +1240,9 @@ LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, // Data map LLMarketplaceData::LLMarketplaceData() : mMarketPlaceStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED), + mMarketPlaceDataFetched(MarketplaceFetchCodes::MARKET_FETCH_NOT_DONE), mStatusUpdatedSignal(NULL), + mDataFetchedSignal(NULL), mDirtyCount(false) { mInventoryObserver = new LLMarketplaceInventoryObserver; @@ -1212,13 +1270,25 @@ void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& else { // Initiate SLM connection and set responder - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING; std::string url = getSLMConnectURL("/merchant"); - log_SLM_infos("LLHTTPClient::get", url, ""); - LLHTTPClient::get(url, new LLSLMGetMerchantResponder(), LLSD()); + if (url != "") + { + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING; + log_SLM_infos("LLHTTPClient::get", url, ""); + LLHTTPClient::get(url, LLSD(), new LLSLMGetMerchantResponder()); + } } } +void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot_type& cb) +{ + if (mDataFetchedSignal == NULL) + { + mDataFetchedSignal = new status_updated_signal_t(); + } + mDataFetchedSignal->connect(cb); +} + // Get/Post/Put requests to the SLM Server using the SLM API void LLMarketplaceData::getSLMListings() { @@ -1248,19 +1318,21 @@ void LLMarketplaceData::getSLMListing(S32 listing_id) LLHTTPClient::get(url, new LLSLMGetListingResponder(folder_id), headers); } -void LLMarketplaceData::createSLMListing(const LLUUID& folder_id) +void LLMarketplaceData::createSLMListing(const LLUUID& folder_id, const LLUUID& version_id, S32 count) { AIHTTPHeaders headers; headers.addHeader("Accept", "application/json"); headers.addHeader("Content-Type", "application/json"); - LLViewerInventoryCategory* category = gInventory.getCategory(folder_id); - + // Build the json message Json::Value root; Json::FastWriter writer; + LLViewerInventoryCategory* category = gInventory.getCategory(folder_id); root["listing"]["name"] = category->getName(); - root["listing"]["inventory_info"]["listing_folder_id"] = category->getUUID().asString(); + root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); + root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString(); + root["listing"]["inventory_info"]["count_on_hand"] = count; std::string json_str = writer.write(root); @@ -1285,11 +1357,18 @@ void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id, S32 listing_id Json::Value root; Json::FastWriter writer; + // Note : auto unlist if the count is 0 (out of stock) + if (is_listed && (count == 0)) + { + is_listed = false; + LLNotificationsUtil::add("AlertMerchantStockFolderEmpty"); + } + // Note : we're assuming that sending unchanged info won't break anything server side... root["listing"]["id"] = listing_id; root["listing"]["is_listed"] = is_listed; - root["listing"]["inventory_info"]["listing_folder_id"] = folder_id; - root["listing"]["inventory_info"]["version_folder_id"] = version_id; + root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); + root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString(); root["listing"]["inventory_info"]["count_on_hand"] = count; std::string json_str = writer.write(root); @@ -1303,10 +1382,10 @@ void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id, S32 listing_id std::string url = getSLMConnectURL("/listing/") + llformat("%d", listing_id); log_SLM_infos("LLHTTPClient::putRaw", url, json_str); setUpdating(folder_id, true); - LLHTTPClient::putRaw(url, data, size, new LLSLMCreateListingsResponder(folder_id, is_listed, version_id), headers); + LLHTTPClient::putRaw(url, data, size, new LLSLMUpdateListingsResponder(folder_id, is_listed, version_id), headers); } -void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& source_folder_id) +void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, const LLUUID& source_folder_id) { AIHTTPHeaders headers; headers.addHeader("Accept", "application/json"); @@ -1317,10 +1396,8 @@ void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing // Note : we're assuming that sending unchanged info won't break anything server side... root["listing"]["id"] = listing_id; - root["listing"]["is_listed"] = false; root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString(); - root["listing"]["inventory_info"]["version_folder_id"] = LLUUID::null.asString(); - root["listing"]["inventory_info"]["count_on_hand"] = -1; + root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString(); std::string json_str = writer.write(root); @@ -1348,7 +1425,7 @@ void LLMarketplaceData::deleteSLMListing(S32 listing_id) log_SLM_infos("LLHTTPClient::del", url, ""); LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); setUpdating(folder_id, true); - LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(folder_id, is_listed, version_id), headers); + LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(folder_id), headers); } std::string LLMarketplaceData::getSLMConnectURL(const std::string& route) @@ -1359,16 +1436,10 @@ std::string LLMarketplaceData::getSLMConnectURL(const std::string& route) { // Get DirectDelivery cap url = regionp->getCapability("DirectDelivery"); - // *TODO : Take this DirectDelivery cap coping mechanism hack out - if (url == "") + if (url != "") { - url = "https://marketplace.secondlife-staging.com/api/1/viewer/" + gAgentID.asString(); + url += route; } - else - { - llinfos << "Merov : DD cap = " << url << ", agent = " << gAgentID.asString() << llendl; - } - url += route; } return url; } @@ -1378,7 +1449,16 @@ void LLMarketplaceData::setSLMStatus(U32 status) mMarketPlaceStatus = status; if (mStatusUpdatedSignal) { - (*mStatusUpdatedSigal)(); + (*mStatusUpdatedSignal)(); + } +} + +void LLMarketplaceData::setSLMDataFetched(U32 status) +{ + mMarketPlaceDataFetched = status; + if (mDataFetchedSignal) + { + (*mDataFetchedSignal)(); } } @@ -1392,13 +1472,29 @@ bool LLMarketplaceData::createListing(const LLUUID& folder_id) return false; } + // Get the version folder: if there is only one subfolder, we will set it as a version folder immediately + S32 count = -1; + LLUUID version_id = getVersionFolderIfUnique(folder_id); + if (version_id.notNull()) + { + count = compute_stock_count(version_id, true); + } + + // Validate the count on hand + if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If the count on hand cannot be evaluated, we will consider it empty (out of stock) at creation time + // It will get reevaluated and updated once the items are fetched + count = 0; + } + // Post the listing creation request to SLM - createSLMListing(folder_id); + createSLMListing(folder_id, version_id, count); return true; } -bool LLMarketplaceData::clearListing(const LLUUID& folder_id) +bool LLMarketplaceData::clearListing(const LLUUID& folder_id, S32 depth) { if (folder_id.isNull()) { @@ -1406,8 +1502,13 @@ bool LLMarketplaceData::clearListing(const LLUUID& folder_id) return false; } + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } // Folder id can be the root of the listing of not so we need to retrieve the root first - S32 depth = depth_nesting_in_marketplace(folder_id); LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth)); S32 listing_id = getListingID(listing_uuid); @@ -1423,7 +1524,7 @@ bool LLMarketplaceData::clearListing(const LLUUID& folder_id) return true; } -bool LLMarketplaceData::getListing(const LLUUID& folder_id) +bool LLMarketplaceData::getListing(const LLUUID& folder_id, S32 depth) { if (folder_id.isNull()) { @@ -1431,8 +1532,13 @@ bool LLMarketplaceData::getListing(const LLUUID& folder_id) return false; } + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } // Folder id can be the root of the listing or not so we need to retrieve the root first - S32 depth = depth_nesting_in_marketplace(folder_id); LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth)); S32 listing_id = getListingID(listing_uuid); @@ -1460,10 +1566,15 @@ bool LLMarketplaceData::getListing(S32 listing_id) return true; } -bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate) +bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate, S32 depth) { + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } // Folder id can be the root of the listing or not so we need to retrieve the root first - 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) @@ -1472,10 +1583,22 @@ bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate) return false; } + if (getActivationState(listing_uuid) == activate) + { + // If activation state is unchanged, no point spamming SLM with an update + return true; + } + LLUUID version_uuid = getVersionFolder(listing_uuid); // Also update the count on hand S32 count = compute_stock_count(folder_id); + if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If the count on hand cannot be evaluated locally, we should not change that SLM value + // We are assuming that this issue is local and should not modify server side values + count = getCountOnHand(listing_uuid); + } // Post the listing update request to SLM updateSLMListing(listing_uuid, listing_id, version_uuid, activate, count); @@ -1483,10 +1606,15 @@ bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate) return true; } -bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id) +bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id, S32 depth) { + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } // Folder id can be the root of the listing or not so we need to retrieve the root first - 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) @@ -1495,11 +1623,23 @@ bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& return false; } + if (getVersionFolder(listing_uuid) == version_id) + { + // If version folder is unchanged, no point spamming SLM with an update + return true; + } + // Note: if the version_id is cleared, we need to unlist the listing, otherwise, state unchanged bool is_listed = (version_id.isNull() ? false : getActivationState(listing_uuid)); // Also update the count on hand S32 count = compute_stock_count(version_id); + if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If the count on hand cannot be evaluated, we will consider it empty (out of stock) when resetting the version folder + // It will get reevaluated and updated once the items are fetched + count = 0; + } // Post the listing update requesst to SLM updateSLMListing(listing_uuid, listing_id, version_id, is_listed, count); @@ -1507,10 +1647,15 @@ bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& return true; } -bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id) +bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id, S32 depth) { + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } // Folder id can be the root of the listing or not so we need to retrieve the root first - 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) @@ -1519,16 +1664,31 @@ bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id) return false; } - // Get the unchanged values - bool is_listed = getActivationState(lissting_uuid); - LLUUID version_uuid = getVersionFolder(listing_uuid); - // Compute the new count on hand S32 count = compute_stock_count(folder_id); + if (count == getCountOnHand(listing_uuid)) + { + // If count on hand is unchanged, no point spamming SLM with an update + return true; + } + else if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If local count on hand is not known at that point, do *not* force an update to SLM + return false; + } + + // Get the unchanged values + bool is_listed = getActivationState(listing_uuid); + LLUUID version_uuid = getVersionFolder(listing_uuid); + // Post the listing update request to SLM updateSLMListing(listing_uuid, listing_id, version_uuid, is_listed, count); + // Force the local value as it prevents spamming (count update may occur in burst when restocking) + // Note that if SLM has a good reason to return a different value, it'll be updated by the responder + setCountOnHand(listing_uuid, count, false); + return true; } @@ -1540,38 +1700,38 @@ bool LLMarketplaceData::associateListing(const LLUUID& folder_id, const LLUUID& return false; } - // Post the listing update request to SLM - associateSLMListing(folder_id, listing_id, source_folder_id); + // Get the version folder: if there is only one subfolder, we will set it as a version folder immediately + LLUUID version_id = getVersionFolderIfUnique(folder_id); + + // Post the listing associate request to SLM + associateSLMListing(folder_id, listing_id, version_id, source_folder_id); return true; } // Methods privately called or called by SLM responders to perform changes -bool LLMarketplaceData::addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, bool update) +bool LLMarketplaceData::addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, const std::string& edit_url, S32 count) { - if (isListed(folder_id)) - { - // Listing already exists -> exit with error - return false; - } mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id, listing_id, version_id, is_listed); - if (update) + mMarketplaceItems[folder_id].mEditURL = edit_url; + mMarketplaceItems[folder_id].mCountOnHand = count; + if (version_id.notNull()) { - update_marketplace_category(folder_id, false); - gInventory.notifyObservers(); + mVersionFolders[version_id] = folder_id; } return true; } bool LLMarketplaceData::deleteListing(const LLUUID& folder_id, bool update) { - if (!isListed(folder_id)) + LLUUID version_folder = getVersionFolder(folder_id); + + if (mMarketplaceItems.erase(folder_id) != 1) { - // Listing doesn't exist -> exit with error return false; } - mMarketplaceItems.erase(folder_id); + mVersionFolders.erase(version_folder); if (update) { @@ -1596,20 +1756,20 @@ bool LLMarketplaceData::deleteListing(S32 listing_id, bool update) bool LLMarketplaceData::getActivationState(const LLUUID& folder_id) { // Listing folder case + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); 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()) + // Version folder case + version_folders_list_t::iterator it_version = mVersionFolders.find(folder_id); + if (it_version != mVersionFolders.end()) { - if ((it->second).mVersionFolderId == folder_id) + marketplace_items_list_t::iterator it = mMarketplaceItems.find(it_version->second); + if (it != mMarketplaceItems.end()) { return (it->second).mIsActive; } - it++; } return false; } @@ -1646,9 +1806,15 @@ LLUUID LLMarketplaceData::getListingFolder(S32 listing_id) } } -std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id) +std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id, S32 depth) { - S32 depth = depth_nesting_in_marketplace(folder_id); + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + LLUUID listing_uuid = nested_parent_id(folder_id, depth); marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); @@ -1661,44 +1827,53 @@ bool LLMarketplaceData::isListed(const LLUUID& folder_id) return (it != mMarketplaceItems.end()); } -bool LLMarketplaceData:isListedAndActive(const LLUUID& folder_id) +bool LLMarketplaceData::isListedAndActive(const LLUUID& folder_id) { return (isListed(folder_id) && getActivationState(folder_id)); } 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; + version_folders_list_t::iterator it = mVersionFolders.find(folder_id); + return (it != mVersionFolders.end()); } -bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id) +bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id, S32 depth) { - S32 depth = depth_nesting_in_marketplace(obj_id); + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(obj_id); + + } + LLUUID listing_uuid = nested_parent_id(obj_id, depth); bool active = getActivationState(listing_uuid); LLUUID version_uuid = getVersionFolder(listing_uuid); return (active && ((obj_id == version_uuid) || gInventory.isObjectDescendentOf(obj_id, version_uuid))); } -LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& obj_id) +LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& folder_id, S32 depth) { - S32 depth = depth_nesting_in_marketplace(obj_id); - LLUUID listing_uuid = nested_parent_id(obj_id, depth); + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + + LLUUID listing_uuid = nested_parent_id(folder_id, depth); return (getActivationState(listing_uuid) ? getVersionFolder(listing_uuid) : LLUUID::null); } -bool LLMarketplaceData::isUpdating(const LLUUID& folder_id) +bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth) { - S32 depth = depth_nesting_in_marketplace(folder_id); + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } if ((depth <= 0) || (depth > 2)) { // Only listing and version folders though are concerned by that status @@ -1736,6 +1911,28 @@ void LLMarketplaceData::setUpdating(const LLUUID& folder_id, bool isUpdating) } } +void LLMarketplaceData::setValidationWaiting(const LLUUID& folder_id, S32 count) +{ + mValidationWaitingList[folder_id] = count; +} + +void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id, S32 count) +{ + waiting_list_t::iterator found = mValidationWaitingList.find(folder_id); + if (found != mValidationWaitingList.end()) + { + found->second -= count; + if (found->second <= 0) + { + mValidationWaitingList.erase(found); + LLInventoryCategory *cat = gInventory.getCategory(folder_id); + validate_marketplacelistings(cat); + update_marketplace_category(folder_id); + gInventory.notifyObservers(); + } + } +} + // Private Modifiers bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id, bool update) { @@ -1783,6 +1980,11 @@ bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id, const LLUUID } (it->second).mVersionFolderId = version_id; + mVersionFolders.erase(old_version_id); + if (version_id.notNull()) + { + mVersionFolders[version_id] = folder_id; + } if (update) { @@ -1811,7 +2013,7 @@ bool LLMarketplaceData::setActivationState(const LLUUID& folder_id, bool activat return true; } -bool LLMarketplaceData:setListingURL(const LLUUID& folder_id, const std::string& edit_url, bool update) +bool LLMarketplaceData::setListingURL(const LLUUID& folder_id, const std::string& edit_url, bool update) { marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); if (it == mMarketplaceItems.end()) diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h index 0ce9083eb..96277445d 100644 --- a/indra/newview/llmarketplacefunctions.h +++ b/indra/newview/llmarketplacefunctions.h @@ -71,13 +71,24 @@ namespace MarketplaceStatusCodes MARKET_PLACE_NOT_INITIALIZED = 0, MARKET_PLACE_INITIALIZING = 1, MARKET_PLACE_CONNECTION_FAILURE = 2, - MARKET_PLACE_MERCHANT = 3, - MARKET_PLACE_NOT_MERCHANT = 4, + MARKET_PLACE_NOT_MERCHANT = 3, + MARKET_PLACE_MERCHANT = 4, MARKET_PLACE_NOT_MIGRATED_MERCHANT = 5, MARKET_PLACE_MIGRATED_MERCHANT = 6 }; } +namespace MarketplaceFetchCodes +{ + enum sCode + { + MARKET_FETCH_NOT_DONE = 0, + MARKET_FETCH_LOADING = 1, + MARKET_FETCH_FAILED = 2, + MARKET_FETCH_DONE = 3 + }; +} + class LLMarketplaceInventoryImporter : public LLSingleton @@ -159,6 +170,7 @@ private: // * The mListingFolderId is used as a key to this map. It could therefore be taken off the LLMarketplaceTuple object themselves. // * The SLM DB however uses the mListingId as its primary key and it shows in its API. In the viewer though, the mListingFolderId is what we use to grab an inventory record. typedef std::map marketplace_items_list_t; +typedef std::map version_folders_list_t; // Session cache of all Marketplace tuples // Notes: @@ -192,17 +204,21 @@ public: typedef boost::signals2::signal status_updated_signal_t; void initializeSLM(const status_updated_signal_t::slot_type& cb); U32 getSLMStatus() const { return mMarketPlaceStatus; } + void setSLMStatus(U32 status); void getSLMListings(); bool isEmpty() const { return (mMarketplaceItems.size() == 0); } + void setDataFetchedSignal(const status_updated_signal_t::slot_type& cb); + void setSLMDataFetched(U32 status); + U32 getSLMDataFetched() { return mMarketPlaceDataFetched; } // High level create/delete/set Marketplace data: each method returns true if the function succeeds, false if error bool createListing(const LLUUID& folder_id); - bool activateListing(const LLUUID& folder_id, bool activate); - bool clearListing(const LLUUID& folder_id); - bool setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id); + bool activateListing(const LLUUID& folder_id, bool activate, S32 depth = -1); + bool clearListing(const LLUUID& folder_id, S32 depth = -1); + bool setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id, S32 depth = -1); bool associateListing(const LLUUID& folder_id, const LLUUID& source_folder_id, S32 listing_id); - bool updateCountOnHand(const LLUUID& folder_id); - bool getListing(const LLUUID& folder_id); + bool updateCountOnHand(const LLUUID& folder_id, S32 depth = -1); + bool getListing(const LLUUID& folder_id, S32 depth = -1); bool getListing(S32 listing_id); bool deleteListing(S32 listing_id, bool update = true); @@ -210,15 +226,15 @@ public: bool isListed(const LLUUID& folder_id); // returns true if folder_id is a Listing folder bool isListedAndActive(const LLUUID& folder_id); // returns true if folder_id is an active (listed) Listing folder bool isVersionFolder(const LLUUID& folder_id); // returns true if folder_id is a Version folder - bool isInActiveFolder(const LLUUID& obj_id); // returns true if the obj_id is buried in an active version folder - LLUUID getActiveFolder(const LLUUID& obj_id); // returns the UUID of the active version folder obj_id is in - bool isUpdating(const LLUUID& folder_id); // returns true if we're waiting from SLM incoming data for folder_id + bool isInActiveFolder(const LLUUID& obj_id, S32 depth = -1); // returns true if the obj_id is buried in an active version folder + LLUUID getActiveFolder(const LLUUID& obj_id, S32 depth = -1); // returns the UUID of the active version folder obj_id is in + bool isUpdating(const LLUUID& folder_id, S32 depth = -1); // returns true if we're waiting from SLM incoming data for folder_id // Access MArketplace data set : each method returns a default value if the argument can't be found bool getActivationState(const LLUUID& folder_id); S32 getListingID(const LLUUID& folder_id); LLUUID getVersionFolder(const LLUUID& folder_id); - std::string getListingURL(const LLUUID& folder_id); + std::string getListingURL(const LLUUID& folder_id, S32 depth = -1); LLUUID getListingFolder(S32 listing_id); S32 getCountOnHand(const LLUUID& folder_id); @@ -227,10 +243,14 @@ public: void setDirtyCount() { mDirtyCount = true; } void setUpdating(const LLUUID& folder_id, bool isUpdating); + // Used to decide when to run a validation on listing folders + void setValidationWaiting(const LLUUID& folder_id, S32 count); + void decrementValidationWaiting(const LLUUID& folder_id, S32 count = 1); + private: // Modify Marketplace data set : each method returns true if the function succeeds, false if error // Used internally only by SLM Responders when data are received from the SLM Server - bool addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, bool update = true); + bool addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, const std::string& edit_url, S32 count); bool deleteListing(const LLUUID& folder_id, bool update = true); bool setListingID(const LLUUID& folder_id, S32 listing_id, bool update = true); bool setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id, bool update = true); @@ -239,25 +259,32 @@ private: bool setCountOnHand(const LLUUID& folder_id, S32 count, bool update = true); // Private SLM API : package data and get/post/put requests to the SLM Server through the SLM API - void setSLMStatus(U32 status); - void createSLMListing(const LLUUID& folder_id); + void createSLMListing(const LLUUID& folder_id, const LLUUID& version_id, S32 count); void getSLMListing(S32 listing_id); void updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, S32 count); - void associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id); + void associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, const LLUUID& source_folder_id); void deleteSLMListing(S32 listing_id); std::string getSLMConnectURL(const std::string& route); // Handling Marketplace connection and inventory connection - U32 mMarketPlaceStatus + U32 mMarketPlaceStatus; status_updated_signal_t* mStatusUpdatedSignal; LLInventoryObserver* mInventoryObserver; bool mDirtyCount; // If true, stock count value need to be updated at the next check // Update data + U32 mMarketPlaceDataFetched; + status_updated_signal_t* mDataFetchedSignal; std::set mPendingUpdateSet; + // Listing folders waiting for validation + typedef std::map waiting_list_t; + waiting_list_t mValidationWaitingList; + // The cache of SLM data (at last...) marketplace_items_list_t mMarketplaceItems; + // We need a list (version folder -> listing folder) because such reverse lookups are frequent + version_folders_list_t mVersionFolders; }; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index fe23e62fe..f96730da8 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -157,7 +157,7 @@ BOOL LLPanelMainInventory::postBuild() { // "All Items" is the previous only view, so it gets the InventorySortOrder mActivePanel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER)); - mActivePanel->getFilter()->markDefault(); + mActivePanel->getFilter().markDefault(); mActivePanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mActivePanel, _1, _2)); mResortActivePanel = true; } @@ -167,7 +167,7 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSinceLogoff(TRUE); recent_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::RECENTITEMS_SORT_ORDER)); recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - recent_items_panel->getFilter()->markDefault(); + recent_items_panel->getFilter().markDefault(); recent_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, recent_items_panel, _1, _2)); } LLInventoryPanel* worn_items_panel = getChild("Worn Items"); @@ -175,7 +175,7 @@ BOOL LLPanelMainInventory::postBuild() { worn_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::WORNITEMS_SORT_ORDER)); worn_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - worn_items_panel->getFilter()->markDefault(); + worn_items_panel->getFilter().markDefault(); worn_items_panel->setFilterWorn(true); worn_items_panel->setFilterLinks(LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS); worn_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, worn_items_panel, _1, _2)); @@ -196,11 +196,11 @@ BOOL LLPanelMainInventory::postBuild() // Note that the "All Items" and "Worn Items" settings do not persist per-account. if(recent_items_panel) { - if(savedFilterState.has(recent_items_panel->getFilter()->getName())) + if(savedFilterState.has(recent_items_panel->getFilter().getName())) { LLSD recent_items = savedFilterState.get( - recent_items_panel->getFilter()->getName()); - recent_items_panel->getFilter()->fromLLSD(recent_items); + recent_items_panel->getFilter().getName()); + recent_items_panel->getFilter().fromLLSD(recent_items); } } } @@ -238,37 +238,28 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) LLInventoryPanel* all_items_panel = getChild("All Items"); if (all_items_panel) { - LLInventoryFilter* filter = all_items_panel->getFilter(); - if (filter) - { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; - } + LLInventoryFilter& filter = all_items_panel->getFilter(); + LLSD filterState; + filter.toLLSD(filterState); + filterRoot[filter.getName()] = filterState; } LLInventoryPanel* recent_items_panel = getChild("Recent Items"); if (recent_items_panel) { - LLInventoryFilter* filter = recent_items_panel->getFilter(); - if (filter) - { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; - } + LLInventoryFilter& filter = recent_items_panel->getFilter(); + LLSD filterState; + filter.toLLSD(filterState); + filterRoot[filter.getName()] = filterState; } LLInventoryPanel* worn_items_panel = getChild("Worn Items"); if (worn_items_panel) { - LLInventoryFilter* filter = worn_items_panel->getFilter(); - if (filter) - { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; - } + LLInventoryFilter& filter = worn_items_panel->getFilter(); + LLSD filterState; + filter.toLLSD(filterState); + filterRoot[filter.getName()] = filterState; } std::ostringstream filterSaveName; @@ -496,7 +487,7 @@ void LLPanelMainInventory::updateSortControls() void LLPanelMainInventory::resetFilters() { LLFloaterInventoryFinder *finder = getFinder(); - getActivePanel()->getFilter()->resetDefault(); + getActivePanel()->getFilter().resetDefault(); if (finder) { finder->updateElementsFromFilter(); @@ -732,7 +723,7 @@ void LLPanelMainInventory::onFilterSelected() return; } - LLInventoryFilter* filter = mActivePanel->getFilter(); + LLInventoryFilter* filter = &mActivePanel->getFilter(); LLFloaterInventoryFinder *finder = getFinder(); if (finder) { @@ -837,7 +828,7 @@ void LLPanelMainInventory::updateItemcountText() } void LLPanelMainInventory::setFilterTextFromFilter() { - mFilterText = mActivePanel->getFilter()->getFilterText(); + mFilterText = mActivePanel->getFilter().getFilterText(); } void LLPanelMainInventory::toggleFindOptions() @@ -899,7 +890,7 @@ LLFloaterInventoryFinder::LLFloaterInventoryFinder(const std::string& name, INV_FINDER_WIDTH, INV_FINDER_HEIGHT, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES), mPanelMainInventory(inventory_view), - mFilter(inventory_view->getPanel()->getFilter()) + mFilter(&inventory_view->getPanel()->getFilter()) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml"); @@ -978,9 +969,6 @@ void LLFloaterInventoryFinder::changeFilter(LLInventoryFilter* filter) void LLFloaterInventoryFinder::updateElementsFromFilter() { - if (!mFilter) - return; - // Get data needed for filter display U32 filter_types = mFilter->getFilterObjectTypes(); std::string filter_string = mFilter->getFilterSubString(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 5ff19b8d1..01528fefb 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2340,10 +2340,8 @@ bool idle_startup() display_startup(); - // Init SLM Marketplace connection so we know which UI should be used for the user as a merchant - // Note: Eventually, all merchant will be migrated to the new SLM system and there will be no reason to show the old UI at all. - // *TODO : Suppress the Merchant Outbox UI completely - check_merchant_status(); + // *TODO : Uncomment that line once the whole grid migrated to SLM and suppress it from LLAgent::handleTeleportFinished() (llagent.cpp) + //check_merchant_status(); display_startup(); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 00c4e3460..fd6dc3188 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -42,7 +42,7 @@ #include "llappearancemgr.h" #include "lldictionary.h" #include "llfirstuse.h" -#include "llfloaterinventory.h" +#include "llfloateravatarpicker.h" #include "llfloatertools.h" #include "llgesturemgr.h" #include "llgiveinventory.h" @@ -52,40 +52,26 @@ #include "llinventorybridge.h" #include "llinventorydefines.h" #include "llinventoryfunctions.h" -#include "llinventorypanel.h" -#include "llmutelist.h" -#include "llnotify.h" +#include "llparcel.h" // Rez under Land Group #include "llpreviewnotecard.h" +#include "llrootview.h" #include "llselectmgr.h" #include "lltoolmgr.h" #include "lltrans.h" -#include "llviewertexturelist.h" -#include "llviewerinventory.h" -#include "llviewerobject.h" #include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" // Rez under Land Group #include "llviewerregion.h" #include "llviewerstats.h" #include "llviewerwindow.h" #include "llvoavatarself.h" -#include "llvolume.h" #include "llworld.h" #include "llpanelface.h" -// -#include "llappviewer.h" // System Folders -#include "llparcel.h" // always rez -#include "llviewerparcelmgr.h" // always rez -// // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1) #include "rlvhandler.h" #include "rlvlocks.h" // [/RLVa:KB] -// MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES -// or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a -// bit from there to give some pad. -const S32 MAX_ITEMS = 42; - // syntactic sugar #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) @@ -288,15 +274,15 @@ void LLCategoryDropDescendentsObserver::done() std::set unique_ids; for(S32 i = 0; i < count; ++i) { - unique_ids.insert(items.at(i)->getUUID()); + unique_ids.insert(items.get(i)->getUUID()); } uuid_vec_t ids; std::back_insert_iterator copier(ids); std::copy(unique_ids.begin(), unique_ids.end(), copier); LLCategoryDropObserver* dropper; - dropper = new LLCategoryDropObserver(mObjectID, mSource); - dropper->fetchItems(ids); - if(dropper->isEverythingComplete()) + dropper = new LLCategoryDropObserver(ids, mObjectID, mSource); + dropper->startFetch(); + if (dropper->isDone()) { dropper->done(); } @@ -360,14 +346,15 @@ LLToolDragAndDrop::LLDragAndDropDictionary::LLDragAndDropDictionary() }; LLToolDragAndDrop::LLToolDragAndDrop() -: LLTool(std::string("draganddrop"), NULL), - mDragStartX(0), - mDragStartY(0), - mSource(SOURCE_AGENT), - mCursor(UI_CURSOR_NO), - mLastAccept(ACCEPT_NO), - mDrop(FALSE), - mCurItemIndex(0) +: LLTool(std::string("draganddrop"), NULL), + mCargoCount(0), + mDragStartX(0), + mDragStartY(0), + mSource(SOURCE_AGENT), + mCursor(UI_CURSOR_NO), + mLastAccept(ACCEPT_NO), + mDrop(FALSE), + mCurItemIndex(0) { } @@ -524,6 +511,7 @@ void LLToolDragAndDrop::beginMultiDrag( void LLToolDragAndDrop::endDrag() { + mEndDragSignal(); LLSelectMgr::getInstance()->unhighlightAll(); setMouseCapture(FALSE); } @@ -580,6 +568,8 @@ ECursorType LLToolDragAndDrop::acceptanceToCursor( EAcceptance acceptance ) mCursor = UI_CURSOR_NOLOCKED; break; + + case ACCEPT_NO: mCursor = UI_CURSOR_NO; break; @@ -674,33 +664,41 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, sOperationId++; } + // For people drag and drop we don't need an actual inventory object, + // instead we need the current cargo id, which should be a person id. + bool is_uuid_dragged = (mSource == SOURCE_PEOPLE); + if(top_view) { handled = TRUE; for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); - EAcceptance item_acceptance = ACCEPT_NO; handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); - if (handled) - { - // use sort order to determine priority of acceptance - *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); - } } - else + else if (is_uuid_dragged) { - return; + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } + if (handled) + { + // use sort order to determine priority of acceptance + *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); } } @@ -717,20 +715,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + EAcceptance item_acceptance; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - - EAcceptance item_acceptance; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } if (handled) @@ -747,20 +752,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); - if (!cargo) + // fix for EXT-3191 + if (cargo) { - handled = FALSE; - break; + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)cargo, + &item_acceptance, + mToolTipMsg); + } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); } - - EAcceptance item_acceptance = ACCEPT_NO; - handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, - mCargoTypes[mCurItemIndex], - (void*)cargo, - &item_acceptance, - mToolTipMsg); if (handled) { // use sort order to determine priority of acceptance @@ -780,17 +792,25 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + EAcceptance item_acceptance; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - EAcceptance item_acceptance; handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } @@ -944,8 +964,7 @@ void LLToolDragAndDrop::pick(const LLPickInfo& pick_info) const S32 item_index = mCurItemIndex; const EDragAndDropType dad_type = mCargoTypes[item_index]; // Call the right implementation function - (U32)callMemberFunction(*this, - LLDragAndDropDictionary::instance().get(dad_type, target)) + callMemberFunction(*this, LLDragAndDropDictionary::instance().get(dad_type, target)) (hit_obj, hit_face, pick_info.mKeyMask, TRUE); } } @@ -1185,6 +1204,7 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj, { switch (LLSelectMgr::getInstance()->getTextureChannel()) { + case 0: default: { @@ -1393,19 +1413,21 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target, msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - // Alway rez objects as land group if available. - if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup")) + // Rez under Land Group + static LLCachedControl AlchemyRezUnderLandGroup(gSavedSettings, "AscentAlwaysRezInGroup"); + LLUUID group_id = gAgent.getGroupID(); + if (AlchemyRezUnderLandGroup) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(gAgent.isInGroup(parcel->getGroupID())) - msg->addUUIDFast(_PREHASH_GroupID, parcel->getGroupID()); - else if(gAgent.isInGroup(parcel->getOwnerID())) - msg->addUUIDFast(_PREHASH_GroupID, parcel->getOwnerID()); - else - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if (gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); } - else - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + + msg->addUUIDFast(_PREHASH_GroupID, group_id); msg->nextBlock("RezData"); // if it's being rezzed from task inventory, we need to enable @@ -1468,6 +1490,7 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target, gInventory.deleteObject(item->getUUID()); gInventory.notifyObservers(); } + // if (gSavedSettings.getBOOL("BroadcastViewerEffects")) { @@ -1481,6 +1504,7 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target, // } // + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_REZ_COUNT); } @@ -1536,6 +1560,7 @@ void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj, // *FIX: only show this if panel not expanded? gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS); } + // if (gSavedSettings.getBOOL("BroadcastViewerEffects")) { @@ -1552,18 +1577,6 @@ void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj, gFloaterTools->dirty(); } -struct LLGiveInventoryInfo -{ - LLUUID mToAgentID; - LLUUID mInventoryObjectID; - LLUUID mIMSessionID; - LLGiveInventoryInfo(const LLUUID& to_agent, const LLUUID& obj_id, const LLUUID &im_session_id = LLUUID::null) : - mToAgentID(to_agent), - mInventoryObjectID(obj_id), - mIMSessionID(im_session_id) - {} -}; - // accessor that looks at permissions, copyability, and names of // inventory items to determine if a drop would be ok. EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item) @@ -1572,9 +1585,7 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL if(!item || !obj) return ACCEPT_NO; // HACK: downcast LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item; - if(!vitem->isFinished()) return ACCEPT_NO; - if (vitem->getIsLinkType()) return ACCEPT_NO; // No giving away links // deny attempts to drop from an object onto itself. This is to @@ -1658,12 +1669,87 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL } +static void give_inventory_cb(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + // if Cancel pressed + if (option == 1) + { + return; + } + + LLSD payload = notification["payload"]; + const LLUUID& session_id = payload["session_id"]; + const LLUUID& agent_id = payload["agent_id"]; + LLViewerInventoryItem * inv_item = gInventory.getItem(payload["item_id"]); + LLViewerInventoryCategory * inv_cat = gInventory.getCategory(payload["item_id"]); + if (NULL == inv_item && NULL == inv_cat) + { + llassert( FALSE ); + return; + } + bool successfully_shared; + if (inv_item) + { + successfully_shared = LLGiveInventory::doGiveInventoryItem(agent_id, inv_item, session_id); + } + else + { + successfully_shared = LLGiveInventory::doGiveInventoryCategory(agent_id, inv_cat, session_id); + } + if (successfully_shared) + { + if ("avatarpicker" == payload["d&d_dest"].asString()) + { + if (LLFloaterAvatarPicker::instanceExists()) + LLFloaterAvatarPicker::getInstance()->close(); + } + LLNotificationsUtil::add("ItemsShared"); + } +} + +static void show_object_sharing_confirmation(const std::string name, + LLInventoryObject* inv_item, + const LLSD& dest, + const LLUUID& dest_agent, + const LLUUID& session_id = LLUUID::null) +{ + if (!inv_item) + { + llassert(NULL != inv_item); + return; + } + LLSD substitutions; + substitutions["RESIDENTS"] = name; + substitutions["ITEMS"] = inv_item->getName(); + LLSD payload; + payload["agent_id"] = dest_agent; + payload["item_id"] = inv_item->getUUID(); + payload["session_id"] = session_id; + payload["d&d_dest"] = dest.asString(); + LLNotificationsUtil::add("ShareItemsConfirmation", substitutions, payload, &give_inventory_cb); +} + +static void get_name_cb(const LLUUID& id, + const std::string& full_name, + LLInventoryObject* inv_obj, + const LLSD& dest, + const LLUUID& dest_agent) +{ + show_object_sharing_confirmation(full_name, + inv_obj, + dest, + id, + LLUUID::null); +} + // function used as drag-and-drop handler for simple agent give inventory requests //static bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_id, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept) + EAcceptance* accept, + const LLSD& dest) { // check the type switch(cargo_type) @@ -1768,13 +1854,13 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( } // must not be already wearing it - // - //LLVOAvatar* my_avatar = gAgentAvatarp; - //if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) - //{ - // return ACCEPT_NO; - //} - // + /* + LLVOAvatarSelf* avatar = gAgentAvatarp; + if( !avatar || avatar->isWearingAttachment(item->getUUID()) ) + { + return ACCEPT_NO; + } + */ const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id)) @@ -1842,13 +1928,14 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand( locateInventory(item, cat); if (!item || !item->isFinished()) return ACCEPT_NO; - // - //LLVOAvatar* my_avatar = gAgentAvatarp; - //if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) - //{ - // return ACCEPT_NO; - //} - // + /* + LLVOAvatarSelf* my_avatar = gAgentAvatarp; + if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) + { + return ACCEPT_NO; + } + */ + EAcceptance accept; BOOL remove_inventory; @@ -1917,13 +2004,13 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject( LLViewerInventoryCategory* cat; locateInventory(item, cat); if(!item || !item->isFinished()) return ACCEPT_NO; - // - //LLVOAvatar* my_avatar = gAgentAvatarp; - //if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) - //{ - // return ACCEPT_NO; - //} - // + /* + LLVOAvatarSelf* my_avatar = gAgentAvatarp; + if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) + { + return ACCEPT_NO; + } + */ if((mask & MASK_CONTROL)) { @@ -2026,23 +2113,20 @@ EAcceptance LLToolDragAndDrop::dad3dApplyToObject( { LL_DEBUGS() << "LLToolDragAndDrop::dad3dApplyToObject()" << LL_ENDL; - // + /* // Fuck this - // *HACK: In order to resolve SL-22177, we need to block drags // from notecards and objects onto other objects. - //if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource)) - //{ - // return ACCEPT_NO; - //} - // + if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource)) + { + return ACCEPT_NO; + } + */ LLViewerInventoryItem* item; LLViewerInventoryCategory* cat; locateInventory(item, cat); - if(!item || !item->isFinished()) return ACCEPT_NO; - EAcceptance rv = willObjectAcceptInventory(obj, item); if((mask & MASK_CONTROL)) { @@ -2704,11 +2788,11 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryCategory::cat_array_t& cats, LLViewerInventoryItem::item_array_t& items) { - if(mCargoIDs.count() == 0) return NULL; + if(mCargoIDs.size() == 0) return NULL; if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)) { // The object should be in user inventory. - for (S32 i = 0; i < mCargoIDs.count(); i++) + for (S32 i = 0; i < mCargoIDs.size(); i++) { LLInventoryItem* item = gInventory.getItem(mCargoIDs[i]); if (item) @@ -2732,7 +2816,7 @@ LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryC || (mCargoType == DAD_ROOT_CATEGORY)) { // The object should be in user inventory. - for (S32 i = 0; i < mCargoIDs.count(); i++) + for (S32 i = 0; i < mCargoIDs.size(); i++) { LLInventoryCategory* category = (LLInventoryCategory*)obj->getInventoryObject(mCargoIDs[i]); if (category) @@ -2743,7 +2827,7 @@ LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryC } else { - for (S32 i = 0; i < mCargoIDs.count(); i++) + for (S32 i = 0; i < mCargoIDs.size(); i++) { LLInventoryItem* item = (LLInventoryItem*)obj->getInventoryObject(mCargoIDs[i]); if (item) diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 09de0e6dd..582cf6b1f 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -33,10 +33,10 @@ #ifndef LL_TOOLDRAGANDDROP_H #define LL_TOOLDRAGANDDROP_H +#include "lldictionary.h" #include "lltool.h" #include "llview.h" #include "lluuid.h" -#include "stdenums.h" #include "llassetstorage.h" #include "llpermissions.h" #include "llwindow.h" @@ -50,6 +50,8 @@ class LLPickInfo; class LLToolDragAndDrop : public LLTool, public LLSingleton { public: + typedef boost::signals2::signal enddrag_signal_t; + LLToolDragAndDrop(); // overridden from LLTool @@ -68,7 +70,9 @@ public: SOURCE_AGENT, SOURCE_WORLD, SOURCE_NOTECARD, - SOURCE_LIBRARY + SOURCE_LIBRARY, + SOURCE_VIEWER, + SOURCE_PEOPLE }; void beginDrag(EDragAndDropType type, @@ -86,9 +90,22 @@ public: const LLUUID& getObjectID() const { return mObjectID; } EAcceptance getLastAccept() { return mLastAccept; } - uuid_vec_t::size_type getCargoIDsCount() const { return mCargoIDs.size(); } + boost::signals2::connection setEndDragCallback( const enddrag_signal_t::slot_type& cb ) { return mEndDragSignal.connect(cb); } + + void setCargoCount(U32 count) { mCargoCount = count; } + void resetCargoCount() { mCargoCount = 0; } + U32 getCargoCount() const { return (mCargoCount > 0) ? mCargoCount : mCargoIDs.size(); } + S32 getCargoIndex() const { return mCurItemIndex; } + static S32 getOperationId() { return sOperationId; } + // deal with permissions of object, etc. returns TRUE if drop can + // proceed, otherwise FALSE. + static BOOL handleDropTextureProtections(LLViewerObject* hit_obj, + LLInventoryItem* item, + LLToolDragAndDrop::ESource source, + const LLUUID& src_id); + protected: enum EDropTarget { @@ -100,6 +117,7 @@ protected: DT_COUNT = 5 }; +protected: // dragOrDrop3dImpl points to a member of LLToolDragAndDrop that // takes parameters (LLViewerObject* obj, S32 face, MASK, BOOL // drop) and returns a BOOL if drop is ok @@ -110,11 +128,14 @@ protected: EAcceptance* acceptance); void dragOrDrop3D(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* acceptance); + static void pickCallback(const LLPickInfo& pick_info); void pick(const LLPickInfo& pick_info); protected: + U32 mCargoCount; + S32 mDragStartX; S32 mDragStartY; @@ -135,6 +156,9 @@ protected: BOOL mDrop; S32 mCurItemIndex; std::string mToolTipMsg; + std::string mCustomMsg; + + enddrag_signal_t mEndDragSignal; protected: // 3d drop functions. these call down into the static functions @@ -209,13 +233,6 @@ protected: // inventory items to determine if a drop would be ok. static EAcceptance willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item); - // deal with permissions of object, etc. returns TRUE if drop can - // proceed, otherwise FALSE. - static BOOL handleDropTextureProtections(LLViewerObject* hit_obj, - LLInventoryItem* item, - LLToolDragAndDrop::ESource source, - const LLUUID& src_id); - public: // helper functions static BOOL isInventoryDropAcceptable(LLViewerObject* obj, LLInventoryItem* item) { return (ACCEPT_YES_COPY_SINGLE <= willObjectAcceptInventory(obj, item)); } @@ -253,7 +270,8 @@ public: static bool handleGiveDragAndDrop(LLUUID agent, LLUUID session, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept); + EAcceptance* accept, + const LLSD& dest = LLSD()); // Classes used for determining 3d drag and drop types. private: diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp index 3e2acd2d5..1aaec8e43 100644 --- a/indra/newview/llviewerfoldertype.cpp +++ b/indra/newview/llviewerfoldertype.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llviewerfoldertype.h" +#include "llcontrol.h" #include "lldictionary.h" #include "llmemory.h" #include "llvisualparam.h" diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 6e91e7a99..bc4358a43 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -811,8 +811,13 @@ bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const bool LLViewerInventoryCategory::acceptItem(LLInventoryItem* inv_item) { - bool accept = true; + if (!inv_item) + { + return false; + } + // Only stock folders have limitation on which item they will accept + bool accept = true; if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) { // If the item is copyable (i.e. non stock) do not accept the drop in a stock folder diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 26aa675c0..c712e1ae3 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -620,8 +620,10 @@ void check_merchant_status() { if (!gSavedSettings.getBOOL("InventoryOutboxDisplayBoth")) { - // Hide both merchant related menu items - gMenuHolder->getChild("MerchantOutbox")->setVisible(FALSE); + // Reset the SLM status: we actually want to check again, that's the point of calling check_merchant_status() + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED); + + // Hide SLM related menu item gMenuHolder->getChild("MarketplaceListings")->setVisible(FALSE); // Also disable the toolbar button for Marketplace Listings @@ -630,9 +632,16 @@ void check_merchant_status() // Launch an SLM test connection to get the merchant status LLMarketplaceData::instance().initializeSLM(boost::bind(&set_merchant_SLM_menu)); - // Launch a Merchant Outbox test connection to get the migration status - LLMarketplaceInventoryImporter::instance().setStatusReportCallback(boost::bind(&set_merchant_outbox_menu, _1, _2)); - LLMarketplaceInventoryImporter::instance().initialize(); + // Do the Merchant Outbox init only once per session + if (LLMarketplaceInventoryImporter::instance().getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) + { + // Hide merchant outbox related menu item + gMenuHolder->getChild("MerchantOutbox")->setVisible(FALSE); + + // Launch a Merchant Outbox test connection to get the migration status + LLMarketplaceInventoryImporter::instance().setStatusReportCallback(boost::bind(&set_merchant_outbox_menu, _1, _2)); + LLMarketplaceInventoryImporter::instance().initialize(); + } } } @@ -8866,16 +8875,6 @@ class LLWorldEnableEnvSettings : public view_listener_t } }; -bool canAccessMarketplace(); -class LLMarketplaceEnabled : public LLMemberListener -{ - bool handleEvent(LLPointer, const LLSD& userdata) - { - gMenuHolder->findControl(userdata["control"].asString())->setValue(canAccessMarketplace()); - return true; - } -}; - class SinguCloseAllDialogs : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -9439,7 +9438,6 @@ void initialize_menus() addMenu(new LLWorldVisibleDestinations(), "World.VisibleDestinations"); (new LLWorldEnvSettings())->registerListener(gMenuHolder, "World.EnvSettings"); (new LLWorldEnableEnvSettings())->registerListener(gMenuHolder, "World.EnableEnvSettings"); - addMenu(new LLMarketplaceEnabled, "Marketplace.Enabled"); // Tools menu diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index f3ef19ee9..0a54eec6f 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -164,7 +164,7 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id, mScreenp(NULL), mLastRegionHandle(0), mRegionCrossingCount(0), - mInitialBakesLoaded(false), + //mInitialBakesLoaded(false), // Value outside legal range, so will always be a mismatch the // first time through. mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f)) diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index aa6f37092..68dba270e 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -120,7 +120,7 @@ private: private: LLUUID mInitialBakeIDs[6]; - bool mInitialBakesLoaded; + //bool mInitialBakesLoaded; /******************************************************************************** diff --git a/indra/newview/skins/default/textures/Marketplace_Dropzone_Background.png b/indra/newview/skins/default/textures/Marketplace_Dropzone_Background.png new file mode 100644 index 0000000000000000000000000000000000000000..9478f7b813eccf8a0750a76dc818910eb7c8dd77 GIT binary patch literal 459 zcmeAS@N?(olHy`uVBq!ia0y~yVB`a`H8|LSB)_qU6_AoFag8Vm&QB{TPb^Aha7@Wh zN>%X8O-xS>N=;0uEIgTN1Jsr4>EaktaqG=p$E@Z6iPnoxuDvaNCp5R*_}#IzAdcJx zJ6$_(eQr~s5lh2(o!5k}ivRA1d}-+kLP8WbWz= zo2o|}faVtN&5`9bpLyJRKEw?3HL|?T0dnvE#JMydf4ntL#=pWQ?zO84I>}q#+vcdz z|Gj?i_R4v#K&7E^w=#B|w2{+abnx-Vu)k*mK~Cz2y87Yv42hOc6?@*VhnRHqV++V| zh!sfWy@KzJcVmy-3Vi&r;&lbYBR3yEp5*)Zqoa_>y;{^PgL{T4}J YD4Bd)pg*TR0~k9Dp00i_>zopr0D7s}NB{r; literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 999f60c4a..75d94e90c 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -434,4 +434,6 @@ with the same filename but different name + + diff --git a/indra/newview/skins/default/xui/en-us/floater_marketplace_listings.xml b/indra/newview/skins/default/xui/en-us/floater_marketplace_listings.xml index 8f58146a4..0806a8728 100644 --- a/indra/newview/skins/default/xui/en-us/floater_marketplace_listings.xml +++ b/indra/newview/skins/default/xui/en-us/floater_marketplace_listings.xml @@ -13,7 +13,6 @@ save_rect="true" save_visibility="false" reuse_instance="true"> - Initializing... - - - + + + @@ -396,6 +396,12 @@ name="Merchant Copy" width="128"> + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/menu_marketplace_view.xml b/indra/newview/skins/default/xui/en-us/menu_marketplace_view.xml index cff5ec104..4b3bb8ee1 100644 --- a/indra/newview/skins/default/xui/en-us/menu_marketplace_view.xml +++ b/indra/newview/skins/default/xui/en-us/menu_marketplace_view.xml @@ -4,13 +4,45 @@ left="0" bottom="0" visible="false" mouse_opaque="false"> - - + label="Sort by name" + name="sort_by_name"> + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 3dc5ac5ef..069897ada 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -8,6 +8,10 @@ Always choose this option + + Close + + + + - - - -[PAYLOAD] - - - -[PAYLOAD] - - - -The owner of this parcel has requested the following [MEDIA_TYPE] URL to be loaded by your viewer: - -[URL] - -You may choose to allow or deny the corresponding domain or in-world scripted object server: - -[DOMAIN] - -"Allow" and "Deny" apply only for this session, while "Blacklist" and "Whitelist" apply forever. -
-