phoenix-firestorm/indra/newview/llpreview.cpp

650 lines
17 KiB
C++

/**
* @file llpreview.cpp
* @brief LLPreview class implementation
*
* $LicenseInfo:firstyear=2002&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 "llpreview.h"
#include "lllineeditor.h"
#include "llinventorydefines.h"
#include "llinventorymodel.h"
#include "llresmgr.h"
#include "lltextbox.h"
#include "llfloaterreg.h"
#include "llfocusmgr.h"
#include "lltooldraganddrop.h"
#include "llradiogroup.h"
#include "llassetstorage.h"
#include "llviewerassettype.h"
#include "llviewermessage.h"
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
#include "lldbstrings.h"
#include "llagent.h"
#include "llvoavatarself.h"
#include "llselectmgr.h"
#include "llviewerinventory.h"
#include "llviewerwindow.h"
#include "lltrans.h"
#include "roles_constants.h"
// [SL:KB] - Patch: UI-FloaterSearchReplace | Checked: 2010-11-05 (Catznip-2.3.0a) | Added: Catznip-2.3.0a
#include "llfloatersearchreplace.h"
#include "llpreviewnotecard.h"
#include "llpreviewscript.h"
#include "llscripteditor.h"
// [/SL:KB]
#include "llpreviewanim.h"
#include "llpreviewgesture.h"
#include "llpreviewsound.h"
#include "llpreviewtexture.h"
// Constants
LLPreview::LLPreview(const LLSD& key)
: LLFloater(key),
mItemUUID(key.has("itemid") ? key.get("itemid").asUUID() : key.asUUID()),
mObjectUUID(), // set later by setObjectID()
mCopyToInvBtn( NULL ),
mForceClose(FALSE),
mUserResized(FALSE),
mCloseAfterSave(FALSE),
mAssetStatus(PREVIEW_ASSET_UNLOADED),
mDirty(TRUE),
mSaveDialogShown(FALSE)
{
mAuxItem = new LLInventoryItem;
// don't necessarily steal focus on creation -- sometimes these guys pop up without user action
setAutoFocus(FALSE);
gInventory.addObserver(this);
refreshFromItem();
}
BOOL LLPreview::postBuild()
{
refreshFromItem();
return TRUE;
}
LLPreview::~LLPreview()
{
gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
gInventory.removeObserver(this);
}
void LLPreview::setObjectID(const LLUUID& object_id)
{
mObjectUUID = object_id;
if (getAssetStatus() == PREVIEW_ASSET_UNLOADED)
{
loadAsset();
}
refreshFromItem();
}
void LLPreview::setItem( LLInventoryItem* item )
{
mItem = item;
if (mItem && getAssetStatus() == PREVIEW_ASSET_UNLOADED)
{
loadAsset();
}
refreshFromItem();
}
const LLInventoryItem *LLPreview::getItem() const
{
const LLInventoryItem *item = NULL;
if (mItem.notNull())
{
item = mItem;
}
else if (mObjectUUID.isNull())
{
// [SL:KB] - Patch: UI-Notecards | Checked: 2010-09-11 (Catznip-2.1.2d) | Added: Catznip-2.1.2d
if (LLInventoryType::IT_NONE == mAuxItem->getInventoryType() && mItemUUID.notNull())
item = gInventory.getItem(mItemUUID);
else
item = mAuxItem;
// [/SL:KB]
//if (mItemUUID.notNull())
//{
// // it's an inventory item, so get the item.
// item = gInventory.getItem(mItemUUID);
//}
}
else
{
// it's an object's inventory item.
LLViewerObject* object = gObjectList.findObject(mObjectUUID);
if(object)
{
item = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mItemUUID));
}
}
return item;
}
// Sub-classes should override this function if they allow editing
void LLPreview::onCommit()
{
const LLViewerInventoryItem *item = dynamic_cast<const LLViewerInventoryItem*>(getItem());
if(item)
{
if (!item->isFinished())
{
// We are attempting to save an item that was never loaded
LL_WARNS() << "LLPreview::onCommit() called with mIsComplete == FALSE"
<< " Type: " << item->getType()
<< " ID: " << item->getUUID()
<< LL_ENDL;
return;
}
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setDescription(getChild<LLUICtrl>("desc")->getValue().asString());
std::string new_name = getChild<LLUICtrl>("name")->getValue().asString();
if ( (new_item->getName() != new_name) && !new_name.empty())
{
new_item->rename(getChild<LLUICtrl>("name")->getValue().asString());
}
if(mObjectUUID.notNull())
{
// must be in an object
LLViewerObject* object = gObjectList.findObject(mObjectUUID);
if(object)
{
object->updateInventory(
new_item,
TASK_INVENTORY_ITEM_KEY,
false);
}
}
else if(item->getPermissions().getOwner() == gAgent.getID())
{
new_item->updateServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
// If the item is an attachment that is currently being worn,
// update the object itself.
if( item->getType() == LLAssetType::AT_OBJECT )
{
if (isAgentAvatarValid())
{
LLViewerObject* obj = gAgentAvatarp->getWornAttachment( item->getUUID() );
if( obj )
{
LLSelectMgr::getInstance()->deselectAll();
LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
LLSelectMgr::getInstance()->selectionSetObjectDescription( getChild<LLUICtrl>("desc")->getValue().asString() );
LLSelectMgr::getInstance()->deselectAll();
}
}
}
}
}
}
void LLPreview::changed(U32 mask)
{
mDirty = TRUE;
}
void LLPreview::setNotecardInfo(const LLUUID& notecard_inv_id,
const LLUUID& object_id)
{
mNotecardInventoryID = notecard_inv_id;
mNotecardObjectID = object_id;
}
void LLPreview::draw()
{
LLFloater::draw();
if (mDirty)
{
mDirty = FALSE;
refreshFromItem();
}
}
void LLPreview::refreshFromItem()
{
const LLInventoryItem* item = getItem();
if (!item)
{
return;
}
if (hasString("Title"))
{
LLStringUtil::format_map_t args;
args["[NAME]"] = item->getName();
LLUIString title = getString("Title", args);
setTitle(title.getString());
}
// <FS:Ansariel> Is called from ctor when controls aren't created yet
//getChild<LLUICtrl>("desc")->setValue(item->getDescription());
//getChildView("desc")->setEnabled(canModify(mObjectUUID, item));
LLUICtrl* desc_control = findChild<LLUICtrl>("desc");
if (desc_control)
{
desc_control->setValue(item->getDescription());
desc_control->setEnabled(canModify(mObjectUUID, item));
}
// </FS:Ansariel>
}
// static
BOOL LLPreview::canModify(const LLUUID taskUUID, const LLInventoryItem* item)
{
const LLViewerObject* object = nullptr;
if (taskUUID.notNull())
{
object = gObjectList.findObject(taskUUID);
}
return canModify(object, item);
}
// static
BOOL LLPreview::canModify(const LLViewerObject* object, const LLInventoryItem* item)
{
if (object && !object->permModify())
{
// No permission to edit in-world inventory
return FALSE;
}
return item && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE);
}
// static
void LLPreview::onText(LLUICtrl*, void* userdata)
{
LLPreview* self = (LLPreview*) userdata;
self->onCommit();
}
// static
void LLPreview::onRadio(LLUICtrl*, void* userdata)
{
LLPreview* self = (LLPreview*) userdata;
self->onCommit();
}
// static
void LLPreview::hide(const LLUUID& item_uuid, BOOL no_saving /* = FALSE */ )
{
// <FS:Ansariel> FIRE-14195: Deleting item from inventory doesn't close preview
//LLFloater* floater = LLFloaterReg::findInstance("preview", LLSD(item_uuid));
LLFloater* floater = LLFloaterReg::findInstance("preview_anim", LLSD(item_uuid));
if (!floater) floater = LLFloaterReg::findInstance("preview_gesture", LLSD(item_uuid));
if (!floater) floater = LLFloaterReg::findInstance("preview_notecard", LLSD(item_uuid));
if (!floater) floater = LLFloaterReg::findInstance("preview_script", LLSD(item_uuid));
if (!floater) floater = LLFloaterReg::findInstance("preview_sound", LLSD(item_uuid));
if (!floater) floater = LLFloaterReg::findInstance("preview_texture", LLSD(item_uuid));
// </FS:Ansariel>
if (!floater) floater = LLFloaterReg::findInstance("preview_avatar", LLSD(item_uuid));
LLPreview* preview = dynamic_cast<LLPreview*>(floater);
if (preview)
{
if ( no_saving )
{
preview->mForceClose = TRUE;
}
preview->closeFloater();
}
}
// static
void LLPreview::dirty(const LLUUID& item_uuid)
{
LLFloater* floater = LLFloaterReg::findInstance("preview", LLSD(item_uuid));
if (!floater) floater = LLFloaterReg::findInstance("preview_avatar", LLSD(item_uuid));
LLPreview* preview = dynamic_cast<LLPreview*>(floater);
if(preview)
{
preview->mDirty = TRUE;
}
}
BOOL LLPreview::handleMouseDown(S32 x, S32 y, MASK mask)
{
if(mClientRect.pointInRect(x, y))
{
// No handler needed for focus lost since this class has no
// state that depends on it.
bringToFront(x, y);
gFocusMgr.setMouseCapture(this);
S32 screen_x;
S32 screen_y;
localPointToScreen(x, y, &screen_x, &screen_y );
LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y);
return TRUE;
}
return LLFloater::handleMouseDown(x, y, mask);
}
BOOL LLPreview::handleMouseUp(S32 x, S32 y, MASK mask)
{
if(hasMouseCapture())
{
gFocusMgr.setMouseCapture(NULL);
return TRUE;
}
return LLFloater::handleMouseUp(x, y, mask);
}
BOOL LLPreview::handleHover(S32 x, S32 y, MASK mask)
{
if(hasMouseCapture())
{
S32 screen_x;
S32 screen_y;
const LLInventoryItem *item = getItem();
localPointToScreen(x, y, &screen_x, &screen_y );
if(item
&& item->getPermissions().allowCopyBy(gAgent.getID(),
gAgent.getGroupID())
&& LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y))
{
EDragAndDropType type;
type = LLViewerAssetType::lookupDragAndDropType(item->getType());
LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_LIBRARY;
if(!mObjectUUID.isNull())
{
src = LLToolDragAndDrop::SOURCE_WORLD;
}
else if(item->getPermissions().getOwner() == gAgent.getID())
{
src = LLToolDragAndDrop::SOURCE_AGENT;
}
LLToolDragAndDrop::getInstance()->beginDrag(type,
item->getUUID(),
src,
mObjectUUID);
return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask );
}
}
return LLFloater::handleHover(x,y,mask);
}
void LLPreview::onOpen(const LLSD& key)
{
if (!getFloaterHost() && !getHost() && getAssetStatus() == PREVIEW_ASSET_UNLOADED)
{
loadAsset();
}
// <FS:Ansariel> Multi preview layout fix; Anim, gesture and sound previews can't be resized
if (getHost() &&
(dynamic_cast<LLPreviewAnim*>(this) ||
dynamic_cast<LLPreviewGesture*>(this) ||
dynamic_cast<LLPreviewSound*>(this)))
{
getHost()->setCanResize(FALSE);
}
// </FS:Ansariel>
}
void LLPreview::setAuxItem( const LLInventoryItem* item )
{
if ( mAuxItem )
mAuxItem->copyItem(item);
}
// static
void LLPreview::onBtnCopyToInv(void* userdata)
{
LLPreview* self = (LLPreview*) userdata;
LLInventoryItem *item = self->mAuxItem;
if(item && item->getUUID().notNull())
{
// Copy to inventory
if (self->mNotecardInventoryID.notNull())
{
copy_inventory_from_notecard(LLUUID::null,
self->mNotecardObjectID,
self->mNotecardInventoryID,
item);
}
else if (self->mObjectUUID.notNull())
{
// item is in in-world inventory
LLViewerObject* object = gObjectList.findObject(self->mObjectUUID);
LLPermissions perm(item->getPermissions());
if(object
&&(perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
&& perm.allowTransferTo(gAgent.getID())))
{
// copy to default folder
set_dad_inventory_item(item, LLUUID::null);
object->moveInventory(LLUUID::null, item->getUUID());
}
}
else
{
LLPointer<LLInventoryCallback> cb = NULL;
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
LLUUID::null,
std::string(),
cb);
}
}
self->closeFloater();
}
// static
void LLPreview::onKeepBtn(void* data)
{
LLPreview* self = (LLPreview*)data;
self->closeFloater();
}
// static
void LLPreview::onDiscardBtn(void* data)
{
LLPreview* self = (LLPreview*)data;
const LLInventoryItem* item = self->getItem();
if (!item) return;
self->mForceClose = TRUE;
self->closeFloater();
// Move the item to the trash
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
if (item->getParentUUID() != trash_id)
{
LLInventoryModel::update_list_t update;
LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
update.push_back(old_folder);
LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
update.push_back(new_folder);
gInventory.accountForUpdate(update);
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setParent(trash_id);
// no need to restamp it though it's a move into trash because
// it's a brand new item already.
new_item->updateParentOnServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
}
}
void LLPreview::handleReshape(const LLRect& new_rect, bool by_user)
{
if(by_user
&& (new_rect.getWidth() != getRect().getWidth() || new_rect.getHeight() != getRect().getHeight()))
{
userResized();
}
LLFloater::handleReshape(new_rect, by_user);
}
//
// LLMultiPreview
//
LLMultiPreview::LLMultiPreview()
: LLMultiFloater(LLSD())
{
// start with a rect in the top-left corner ; will get resized
LLRect rect;
// <FS:Ansariel> Multi preview layout fix
//rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 200, 400);
rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 10, 10);
// </FS:Ansariel>
setRect(rect);
LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup("preview");
if (last_floater)
{
stackWith(*last_floater);
}
setTitle(LLTrans::getString("MultiPreviewTitle"));
buildTabContainer();
setCanResize(TRUE);
}
void LLMultiPreview::onOpen(const LLSD& key)
{
// Floater could be something else than LLPreview, eg LLFloaterProfile.
LLPreview* frontmost_preview = dynamic_cast<LLPreview*>(mTabContainer->getCurrentPanel());
if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED)
{
frontmost_preview->loadAsset();
}
LLMultiFloater::onOpen(key);
// <FS:Ansariel> Multi preview layout fix
center();
}
void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user)
{
if(new_rect.getWidth() != getRect().getWidth() || new_rect.getHeight() != getRect().getHeight())
{
// Floater could be something else than LLPreview, eg LLFloaterProfile.
LLPreview* frontmost_preview = dynamic_cast<LLPreview*>(mTabContainer->getCurrentPanel());
if (frontmost_preview)
{
frontmost_preview->userResized();
}
}
LLFloater::handleReshape(new_rect, by_user);
}
void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click)
{
// Floater could be something else than LLPreview, eg LLFloaterProfile.
LLPreview* opened_preview = dynamic_cast<LLPreview*>(opened_floater);
if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED)
{
opened_preview->loadAsset();
}
// <FS:Ansariel> Update preview dimensions for multi texture preview upon tab change
LLPreviewTexture* texture_preview = dynamic_cast<LLPreviewTexture*>(opened_floater);
if (texture_preview)
{
texture_preview->setUpdateDimensions(TRUE);
}
// </FS:Ansariel>
// [SL:KB] - Patch: UI-FloaterSearchReplace | Checked: 2010-11-05 (Catznip-2.3.0a) | Added: Catznip-2.3.0a
LLFloaterSearchReplace* pSearchFloater = LLFloaterReg::getTypedInstance<LLFloaterSearchReplace>("search_replace");
if ( (pSearchFloater) && (pSearchFloater->getDependee() == this) )
{
LLPreviewNotecard* pPreviewNotecard = NULL; LLPreviewLSL* pPreviewScript = NULL; LLLiveLSLEditor* pPreviewScriptLive = NULL;
if ((pPreviewNotecard = dynamic_cast<LLPreviewNotecard*>(opened_preview)) != NULL)
{
LLFloaterSearchReplace::show(pPreviewNotecard->getEditor());
}
else if ((pPreviewScript = dynamic_cast<LLPreviewLSL*>(opened_preview)) != NULL)
{
LLFloaterSearchReplace::show(pPreviewScript->getEditor());
}
else if ((pPreviewScriptLive = dynamic_cast<LLLiveLSLEditor*>(opened_preview)) != NULL)
{
LLFloaterSearchReplace::show(pPreviewScriptLive->getEditor());
}
else
{
pSearchFloater->setVisible(FALSE);
}
}
// [/SL:KB]
}
void LLPreview::setAssetId(const LLUUID& asset_id)
{
const LLViewerInventoryItem* item = dynamic_cast<const LLViewerInventoryItem*>(getItem());
if(NULL == item)
{
return;
}
if(mObjectUUID.isNull())
{
// Update avatar inventory asset_id.
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setAssetUUID(asset_id);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
}
else
{
// Update object inventory asset_id.
LLViewerObject* object = gObjectList.findObject(mObjectUUID);
if(NULL == object)
{
return;
}
object->updateViewerInventoryAsset(item, asset_id);
}
}