/** * @file llpreview.cpp * @brief LLPreview class implementation * * $LicenseInfo:firstyear=2002&license=viewergpl$ * * Copyright (c) 2002-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "stdenums.h" #include "llpreview.h" #include "lllineeditor.h" #include "llinventory.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 "llviewerobject.h" #include "llviewerobjectlist.h" #include "lldbstrings.h" #include "llagent.h" #include "llvoavatarself.h" #include "llselectmgr.h" #include "llfloaterinventory.h" #include "llviewerinventory.h" #include "llviewerwindow.h" #include "lltrans.h" // Constants LLPreview::LLPreview(const LLSD& key) : LLFloater(key), mItemUUID(key.asUUID()), mObjectUUID(), // set later by setObjectID() mCopyToInvBtn( NULL ), mForceClose(FALSE), mUserResized(FALSE), mCloseAfterSave(FALSE), mAssetStatus(PREVIEW_ASSET_UNLOADED), mDirty(TRUE) { 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(); } } void LLPreview::setItem( LLInventoryItem* item ) { mItem = item; if (mItem && getAssetStatus() == PREVIEW_ASSET_UNLOADED) { loadAsset(); } } const LLInventoryItem *LLPreview::getItem() const { const LLInventoryItem *item = NULL; if (mItem.notNull()) { item = mItem; } else if (mObjectUUID.isNull()) { // 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->isComplete()) { // We are attempting to save an item that was never loaded llwarns << "LLPreview::onCommit() called with mIsComplete == FALSE" << " Type: " << item->getType() << " ID: " << item->getUUID() << llendl; return; } LLPointer new_item = new LLViewerInventoryItem(item); new_item->setDescription(childGetText("desc")); 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 ) { LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); if( avatar ) { LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() ); if( obj ) { LLSelectMgr::getInstance()->deselectAll(); LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, FALSE ); LLSelectMgr::getInstance()->selectionSetObjectDescription( childGetText("desc") ); 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()); } childSetText("desc",item->getDescription()); BOOL can_agent_manipulate = item->getPermissions().allowModifyBy(gAgent.getID()); childSetEnabled("desc",can_agent_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 = LLAssetType::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(self->mNotecardObjectID, self->mNotecardInventoryID, item); } 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(); // Delete the item entirely /* item->removeFromServer(); gInventory.deleteObject(item->getUUID()); gInventory.notifyObservers(); */ // Move the item to the trash LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_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() { // *TODO: There should be a .xml file for this const LLRect& nextrect = LLFloaterReg::getFloaterRect("preview"); // place where the next preview should show up if (nextrect.getWidth() > 0) { setRect(nextrect); } else { // start with a rect in the top-left corner ; will get resized LLRect rect; rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeight(), 200, 200); setRect(rect); } setTitle(LLTrans::getString("MultiPreviewTitle")); buildTabContainer(); setCanResize(TRUE); } void LLMultiPreview::onOpen(const LLSD& key) { LLPreview* frontmost_preview = (LLPreview*)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()) { LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); if (frontmost_preview) frontmost_preview->userResized(); } LLFloater::handleReshape(new_rect, by_user); } void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click) { LLPreview* opened_preview = (LLPreview*)opened_floater; if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { opened_preview->loadAsset(); } }