phoenix-firestorm/indra/newview/llfloateroutbox.cpp

620 lines
18 KiB
C++
Executable File

/**
* @file llfloateroutbox.cpp
* @brief Implementation of the merchant outbox window
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llfloateroutbox.h"
#include "llfloaterreg.h"
#include "llfolderview.h"
#include "llinventorybridge.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llinventoryobserver.h"
#include "llinventorypanel.h"
#include "llmarketplacefunctions.h"
#include "llnotificationhandler.h"
#include "llnotificationmanager.h"
#include "llnotificationsutil.h"
#include "lltextbox.h"
#include "lltransientfloatermgr.h"
#include "lltrans.h"
#include "llviewernetwork.h"
#include "llwindowshade.h"
///----------------------------------------------------------------------------
/// LLOutboxNotification class
///----------------------------------------------------------------------------
LLNotificationsUI::LLOutboxNotification::LLOutboxNotification()
: LLSystemNotificationHandler("Outbox", "outbox")
{
}
bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify)
{
LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox");
outbox_floater->showNotification(notify);
return false;
}
void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p)
{
LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
if (notification_handler)
{
notification_handler->onDelete(p);
}
}
///----------------------------------------------------------------------------
/// LLOutboxAddedObserver helper class
///----------------------------------------------------------------------------
class LLOutboxAddedObserver : public LLInventoryCategoryAddedObserver
{
public:
LLOutboxAddedObserver(LLFloaterOutbox * outboxFloater)
: LLInventoryCategoryAddedObserver()
, mOutboxFloater(outboxFloater)
{
}
void done()
{
for (cat_vec_t::iterator it = mAddedCategories.begin(); it != mAddedCategories.end(); ++it)
{
LLViewerInventoryCategory* added_category = *it;
LLFolderType::EType added_category_type = added_category->getPreferredType();
if (added_category_type == LLFolderType::FT_OUTBOX)
{
mOutboxFloater->initializeMarketPlace();
}
}
}
private:
LLFloaterOutbox * mOutboxFloater;
};
///----------------------------------------------------------------------------
/// LLFloaterOutbox
///----------------------------------------------------------------------------
LLFloaterOutbox::LLFloaterOutbox(const LLSD& key)
: LLFloater(key)
, mCategoriesObserver(NULL)
, mCategoryAddedObserver(NULL)
, mImportBusy(false)
, mImportButton(NULL)
, mInventoryFolderCountText(NULL)
, mInventoryImportInProgress(NULL)
, mInventoryPlaceholder(NULL)
, mInventoryText(NULL)
, mInventoryTitle(NULL)
, mOutboxId(LLUUID::null)
, mOutboxItemCount(0)
, mOutboxTopLevelDropZone(NULL)
, mWindowShade(NULL)
{
}
LLFloaterOutbox::~LLFloaterOutbox()
{
if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))
{
gInventory.removeObserver(mCategoriesObserver);
}
delete mCategoriesObserver;
if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver))
{
gInventory.removeObserver(mCategoryAddedObserver);
}
delete mCategoryAddedObserver;
}
BOOL LLFloaterOutbox::postBuild()
{
mInventoryFolderCountText = getChild<LLTextBox>("outbox_folder_count");
mInventoryImportInProgress = getChild<LLView>("import_progress_indicator");
mInventoryPlaceholder = getChild<LLView>("outbox_inventory_placeholder_panel");
mInventoryText = mInventoryPlaceholder->getChild<LLTextBox>("outbox_inventory_placeholder_text");
mInventoryTitle = mInventoryPlaceholder->getChild<LLTextBox>("outbox_inventory_placeholder_title");
mImportButton = getChild<LLButton>("outbox_import_btn");
mImportButton->setCommitCallback(boost::bind(&LLFloaterOutbox::onImportButtonClicked, this));
mOutboxTopLevelDropZone = getChild<LLPanel>("outbox_generic_drag_target");
LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLFloaterOutbox::onFocusReceived, this));
// Observe category creation to catch outbox creation (moot if already existing)
mCategoryAddedObserver = new LLOutboxAddedObserver(this);
gInventory.addObserver(mCategoryAddedObserver);
return TRUE;
}
void LLFloaterOutbox::cleanOutbox()
{
// Note: we cannot delete the mOutboxInventoryPanel as that point
// as this is called through callback observers of the panel itself.
// Doing so would crash rapidly.
// Invalidate the outbox data
mOutboxId.setNull();
mOutboxItemCount = 0;
}
void LLFloaterOutbox::onClose(bool app_quitting)
{
if (mWindowShade)
{
delete mWindowShade;
mWindowShade = NULL;
}
}
void LLFloaterOutbox::onOpen(const LLSD& key)
{
//
// Initialize the Market Place or go update the outbox
//
if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED)
{
initializeMarketPlace();
}
else
{
setupOutbox();
}
//
// Update the floater view
//
updateView();
//
// Trigger fetch of outbox contents
//
fetchOutboxContents();
}
void LLFloaterOutbox::onFocusReceived()
{
fetchOutboxContents();
}
void LLFloaterOutbox::fetchOutboxContents()
{
if (mOutboxId.notNull())
{
LLInventoryModelBackgroundFetch::instance().start(mOutboxId);
}
}
void LLFloaterOutbox::setupOutbox()
{
if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT)
{
// If we are *not* a merchant or we have no market place connection established yet, do nothing
return;
}
// We are a merchant. Get the outbox, create it if needs be.
LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, true);
if (outbox_id.isNull())
{
// We should never get there unless the inventory fails badly
LL_ERRS() << "Inventory problem: failure to create the outbox for a merchant!" << LL_ENDL;
return;
}
// Consolidate Merchant Outbox
// 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(outbox_id, LLFolderType::FT_OUTBOX);
if (outbox_id == mOutboxId)
{
LL_WARNS() << "Inventory warning: Merchant outbox already set" << LL_ENDL;
return;
}
mOutboxId = outbox_id;
// No longer need to observe new category creation
if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver))
{
gInventory.removeObserver(mCategoryAddedObserver);
delete mCategoryAddedObserver;
mCategoryAddedObserver = NULL;
}
llassert(!mCategoryAddedObserver);
// Create observer for outbox modifications : clear the old one and create a new one
if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))
{
gInventory.removeObserver(mCategoriesObserver);
delete mCategoriesObserver;
}
mCategoriesObserver = new LLInventoryCategoriesObserver();
gInventory.addObserver(mCategoriesObserver);
mCategoriesObserver->addCategory(mOutboxId, boost::bind(&LLFloaterOutbox::onOutboxChanged, this));
llassert(mCategoriesObserver);
// Set up the outbox inventory view
LLInventoryPanel* inventory_panel = mOutboxInventoryPanel.get();
if (inventory_panel)
{
delete inventory_panel;
}
inventory_panel = LLUICtrlFactory::createFromFile<LLInventoryPanel>("panel_outbox_inventory.xml", mInventoryPlaceholder->getParent(), LLInventoryPanel::child_registry_t::instance());
mOutboxInventoryPanel = inventory_panel->getInventoryPanelHandle();
llassert(mOutboxInventoryPanel.get() != NULL);
// Reshape the inventory to the proper size
LLRect inventory_placeholder_rect = mInventoryPlaceholder->getRect();
inventory_panel->setShape(inventory_placeholder_rect);
// Set the sort order newest to oldest
inventory_panel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME);
inventory_panel->getFilter().markDefault();
// Get the content of the outbox
fetchOutboxContents();
}
void LLFloaterOutbox::initializeMarketPlace()
{
//
// Initialize the marketplace import API
//
LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance();
if (!importer.isInitialized())
{
importer.setInitializationErrorCallback(boost::bind(&LLFloaterOutbox::initializationReportError, this, _1, _2));
importer.setStatusChangedCallback(boost::bind(&LLFloaterOutbox::importStatusChanged, this, _1));
importer.setStatusReportCallback(boost::bind(&LLFloaterOutbox::importReportResults, this, _1, _2));
importer.initialize();
}
}
void LLFloaterOutbox::setStatusString(const std::string& statusString)
{
llassert(mInventoryFolderCountText != NULL);
mInventoryFolderCountText->setText(statusString);
}
void LLFloaterOutbox::updateFolderCount()
{
if (mOutboxInventoryPanel.get() && mOutboxId.notNull())
{
U32 item_count = 0;
if (mOutboxId.notNull())
{
LLInventoryModel::cat_array_t * cats;
LLInventoryModel::item_array_t * items;
gInventory.getDirectDescendentsOf(mOutboxId, cats, items);
item_count = cats->size() + items->size();
}
mOutboxItemCount = item_count;
}
else
{
// If there's no outbox, the number of items in it should be set to 0 for consistency
mOutboxItemCount = 0;
}
if (!mImportBusy)
{
updateFolderCountStatus();
}
}
void LLFloaterOutbox::updateFolderCountStatus()
{
if (mOutboxInventoryPanel.get() && mOutboxId.notNull())
{
switch (mOutboxItemCount)
{
case 0: setStatusString(getString("OutboxFolderCount0")); break;
case 1: setStatusString(getString("OutboxFolderCount1")); break;
default:
{
std::string item_count_str = llformat("%d", mOutboxItemCount);
LLStringUtil::format_map_t args;
args["[NUM]"] = item_count_str;
setStatusString(getString("OutboxFolderCountN", args));
break;
}
}
}
mImportButton->setEnabled(mOutboxItemCount > 0);
}
void LLFloaterOutbox::updateView()
{
updateFolderCount();
LLInventoryPanel* panel = mOutboxInventoryPanel.get();
if (mOutboxItemCount > 0)
{
panel->setVisible(TRUE);
mInventoryPlaceholder->setVisible(FALSE);
mOutboxTopLevelDropZone->setVisible(TRUE);
}
else
{
if (panel)
{
panel->setVisible(FALSE);
}
// Show the drop zone if there is an outbox folder
mOutboxTopLevelDropZone->setVisible(mOutboxId.notNull());
mInventoryPlaceholder->setVisible(TRUE);
std::string outbox_text;
std::string outbox_title;
std::string outbox_tooltip;
const LLSD& subs = getMarketplaceStringSubstitutions();
U32 mkt_status = LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus();
if (mOutboxId.notNull())
{
// Does the outbox needs recreation?
if ((mOutboxInventoryPanel.get() == NULL) || !gInventory.getCategory(mOutboxId))
{
setupOutbox();
}
// "Outbox is empty!" message strings
outbox_text = LLTrans::getString("InventoryOutboxNoItems", subs);
outbox_title = LLTrans::getString("InventoryOutboxNoItemsTitle");
outbox_tooltip = LLTrans::getString("InventoryOutboxNoItemsTooltip");
}
else if (mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING)
{
// "Initializing!" message strings
outbox_text = LLTrans::getString("InventoryOutboxInitializing", subs);
outbox_title = LLTrans::getString("InventoryOutboxInitializingTitle");
outbox_tooltip = LLTrans::getString("InventoryOutboxInitializingTooltip");
}
else if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT)
{
// "Not a merchant!" message strings
outbox_text = LLTrans::getString("InventoryOutboxNotMerchant", subs);
outbox_title = LLTrans::getString("InventoryOutboxNotMerchantTitle");
outbox_tooltip = LLTrans::getString("InventoryOutboxNotMerchantTooltip");
}
else
{
// "Errors!" message strings
outbox_text = LLTrans::getString("InventoryOutboxError", subs);
outbox_title = LLTrans::getString("InventoryOutboxErrorTitle");
outbox_tooltip = LLTrans::getString("InventoryOutboxErrorTooltip");
}
mInventoryText->setValue(outbox_text);
mInventoryTitle->setValue(outbox_title);
mInventoryPlaceholder->getParent()->setToolTip(outbox_tooltip);
}
}
bool isAccepted(EAcceptance accept)
{
return (accept >= ACCEPT_YES_COPY_SINGLE);
}
BOOL LLFloaterOutbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
EAcceptance* accept,
std::string& tooltip_msg)
{
if ((mOutboxInventoryPanel.get() == NULL) ||
(mWindowShade && mWindowShade->isShown()) ||
LLMarketplaceInventoryImporter::getInstance()->isImportInProgress() ||
mOutboxId.isNull())
{
return FALSE;
}
LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
BOOL handled = (handled_view != NULL);
// Determine if the mouse is inside the inventory panel itself or just within the floater
bool pointInInventoryPanel = false;
bool pointInInventoryPanelChild = false;
LLInventoryPanel* panel = mOutboxInventoryPanel.get();
LLFolderView* root_folder = panel->getRootFolder();
if (panel->getVisible())
{
S32 inv_x, inv_y;
localPointToOtherView(x, y, &inv_x, &inv_y, panel);
pointInInventoryPanel = panel->getRect().pointInRect(inv_x, inv_y);
LLView * inventory_panel_child_at_point = panel->childFromPoint(inv_x, inv_y, true);
pointInInventoryPanelChild = (inventory_panel_child_at_point != root_folder);
}
// Pass all drag and drop for this floater to the outbox inventory control
if (!handled || !isAccepted(*accept))
{
// Handle the drag and drop directly to the root of the outbox if we're not in the inventory panel
// (otherwise the inventory panel itself will handle the drag and drop operation, without any override)
if (!pointInInventoryPanel)
{
handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
}
mOutboxTopLevelDropZone->setBackgroundVisible(handled && !drop && isAccepted(*accept));
}
else
{
mOutboxTopLevelDropZone->setBackgroundVisible(!pointInInventoryPanelChild);
}
return handled;
}
BOOL LLFloaterOutbox::handleHover(S32 x, S32 y, MASK mask)
{
mOutboxTopLevelDropZone->setBackgroundVisible(FALSE);
return LLFloater::handleHover(x, y, mask);
}
void LLFloaterOutbox::onMouseLeave(S32 x, S32 y, MASK mask)
{
mOutboxTopLevelDropZone->setBackgroundVisible(FALSE);
LLFloater::onMouseLeave(x, y, mask);
}
void LLFloaterOutbox::onImportButtonClicked()
{
if (mOutboxInventoryPanel.get())
{
mOutboxInventoryPanel.get()->clearSelection();
}
mImportBusy = LLMarketplaceInventoryImporter::instance().triggerImport();
}
void LLFloaterOutbox::onOutboxChanged()
{
LLViewerInventoryCategory* category = gInventory.getCategory(mOutboxId);
if (mOutboxId.notNull() && category)
{
fetchOutboxContents();
updateView();
}
else
{
cleanOutbox();
}
}
void LLFloaterOutbox::importReportResults(U32 status, const LLSD& content)
{
if (status == MarketplaceErrorCodes::IMPORT_DONE)
{
LLNotificationsUtil::add("OutboxImportComplete");
}
else if (status == MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS)
{
const LLSD& subs = getMarketplaceStringSubstitutions();
LLNotificationsUtil::add("OutboxImportHadErrors", subs);
}
else
{
char status_string[16];
sprintf(status_string, "%d", status);
LLSD subs;
subs["[ERROR_CODE]"] = status_string;
LLNotificationsUtil::add("OutboxImportFailed", subs);
}
updateView();
}
void LLFloaterOutbox::importStatusChanged(bool inProgress)
{
if (mOutboxId.isNull() && (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT))
{
setupOutbox();
}
if (inProgress)
{
if (mImportBusy)
{
setStatusString(getString("OutboxImporting"));
}
else
{
setStatusString(getString("OutboxInitializing"));
}
mImportBusy = true;
mImportButton->setEnabled(false);
mInventoryImportInProgress->setVisible(true);
}
else
{
setStatusString("");
mImportBusy = false;
mImportButton->setEnabled(mOutboxItemCount > 0);
mInventoryImportInProgress->setVisible(false);
}
updateView();
}
void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content)
{
if (status >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST)
{
char status_string[16];
sprintf(status_string, "%d", status);
LLSD subs;
subs["[ERROR_CODE]"] = status_string;
LLNotificationsUtil::add("OutboxInitFailed", subs);
}
updateView();
}
void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification)
{
LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
llassert(notification_handler);
notification_handler->processNotification(notification);
}