/** * @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" // 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()) { 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(object->getInventoryObject(mItemUUID)); } } return item; } // Sub-classes should override this function if they allow editing void LLPreview::onCommit() { const LLViewerInventoryItem *item = dynamic_cast(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 new_item = new LLViewerInventoryItem(item); new_item->setDescription(getChild("desc")->getValue().asString()); std::string new_name = getChild("name")->getValue().asString(); if ( (new_item->getName() != new_name) && !new_name.empty()) { new_item->rename(getChild("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("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()); } getChild("desc")->setValue(item->getDescription()); getChildView("desc")->setEnabled(canModify(mObjectUUID, item)); } // 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 */ ) { LLFloater* floater = LLFloaterReg::findInstance("preview", LLSD(item_uuid)); if (!floater) floater = LLFloaterReg::findInstance("preview_avatar", LLSD(item_uuid)); LLPreview* preview = dynamic_cast(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(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(); } } 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 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 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; rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 200, 400); 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(mTabContainer->getCurrentPanel()); if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { frontmost_preview->loadAsset(); } LLMultiFloater::onOpen(key); } 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(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(opened_floater); if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { opened_preview->loadAsset(); } } void LLPreview::setAssetId(const LLUUID& asset_id) { const LLViewerInventoryItem* item = dynamic_cast(getItem()); if(NULL == item) { return; } if(mObjectUUID.isNull()) { // Update avatar inventory asset_id. LLPointer 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); } }