Files
SingularityViewer/indra/newview/lltoolplacer.cpp
Lirusaito 5080746fa0 Prevent build preferences from altering objects we've duplicated. Offer an off switch for build prefs.
Added LiruEnableBuildPrefs, for turning off, when users do not want to use their default build parameters for a few prims, but don't wish to reset them permanently.
    This should perhaps end up on the build floater somewhere... but for now, debug only.
Note some of this fix for duplication may be overly cautious, but better safe than sorry.
Added a check for physical default in during creation, so that building at great distances adheres a bit more to build preferences.
Added IsCOA into the build settings, since I'd forgotten, previously.
2012-07-14 15:54:36 -04:00

665 lines
21 KiB
C++

/**
* @file lltoolplacer.cpp
* @brief Tool for placing new objects into the world
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-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"
// self header
#include "lltoolplacer.h"
// viewer headers
#include "llbutton.h"
#include "llviewercontrol.h"
#include "llfirstuse.h"
#include "llfloatertools.h"
#include "llselectmgr.h"
#include "llstatusbar.h"
#include "lltoolcomp.h"
#include "lltoolmgr.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
#include "llviewerwindow.h"
#include "llworld.h"
#include "llui.h"
//Headers added for functions moved from viewer.cpp
#include "llvograss.h"
#include "llvotree.h"
#include "llvolumemessage.h"
#include "llhudmanager.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llaudioengine.h"
#include "llhudeffecttrail.h"
#include "llviewerobjectlist.h"
#include "llviewercamera.h"
#include "llviewerstats.h"
#include "llvoavatarself.h"
// linden library headers
#include "llprimitive.h"
#include "llwindow.h"
// <edit>
#include "llparcel.h" // always rez
#include "llviewerparcelmgr.h" // always rez
// </edit>
#include "importtracker.h"
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
const LLVector3 DEFAULT_OBJECT_SCALE(0.5f, 0.5f, 0.5f);
//static
LLPCode LLToolPlacer::sObjectType = LL_PCODE_CUBE;
LLToolPlacer::LLToolPlacer()
: LLTool( std::string("Create") )
{
}
BOOL LLToolPlacer::raycastForNewObjPos( S32 x, S32 y, LLViewerObject** hit_obj, S32* hit_face,
BOOL* b_hit_land, LLVector3* ray_start_region, LLVector3* ray_end_region, LLViewerRegion** region )
{
F32 max_dist_from_camera = gSavedSettings.getF32( "MaxSelectDistance" ) - 1.f;
// Viewer-side pick to find the right sim to create the object on.
// First find the surface the object will be created on.
LLPickInfo pick = gViewerWindow->pickImmediate(x, y, FALSE);
// Note: use the frontmost non-flora version because (a) plants usually have lots of alpha and (b) pants' Havok
// representations (if any) are NOT the same as their viewer representation.
if (pick.mPickType == LLPickInfo::PICK_FLORA)
{
*hit_obj = NULL;
*hit_face = -1;
}
else
{
*hit_obj = pick.getObject();
*hit_face = pick.mObjectFace;
}
*b_hit_land = !(*hit_obj) && !pick.mPosGlobal.isExactlyZero();
LLVector3d land_pos_global = pick.mPosGlobal;
// Make sure there's a surface to place the new object on.
BOOL bypass_sim_raycast = FALSE;
LLVector3d surface_pos_global;
if (*b_hit_land)
{
surface_pos_global = land_pos_global;
bypass_sim_raycast = TRUE;
}
else
if (*hit_obj)
{
surface_pos_global = (*hit_obj)->getPositionGlobal();
}
else
{
return FALSE;
}
// Make sure the surface isn't too far away.
LLVector3d ray_start_global = gAgentCamera.getCameraPositionGlobal();
F32 dist_to_surface_sq = (F32)((surface_pos_global - ray_start_global).magVecSquared());
if( dist_to_surface_sq > (max_dist_from_camera * max_dist_from_camera) )
{
return FALSE;
}
// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-0.2.0f
// NOTE: don't use surface_pos_global since for prims it will be the center of the prim while we need center + offset
if ( (gRlvHandler.hasBehaviour(RLV_BHVR_FARTOUCH)) && (dist_vec_squared(gAgent.getPositionGlobal(), pick.mPosGlobal) > 1.5f * 1.5f) )
{
return FALSE;
}
// [/RLVa:KB]
// Find the sim where the surface lives.
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromPosGlobal(surface_pos_global);
if (!regionp)
{
llwarns << "Trying to add object outside of all known regions!" << llendl;
return FALSE;
}
// Find the simulator-side ray that will be used to place the object accurately
LLVector3d mouse_direction;
mouse_direction.setVec( gViewerWindow->mouseDirectionGlobal( x, y ) );
*region = regionp;
*ray_start_region = regionp->getPosRegionFromGlobal( ray_start_global );
F32 near_clip = LLViewerCamera::getInstance()->getNear() + 0.01f; // Include an epsilon to avoid rounding issues.
*ray_start_region += LLViewerCamera::getInstance()->getAtAxis() * near_clip;
if( bypass_sim_raycast )
{
// Hack to work around Havok's inability to ray cast onto height fields
*ray_end_region = regionp->getPosRegionFromGlobal( surface_pos_global ); // ray end is the viewer's intersection point
}
else
{
LLVector3d ray_end_global = ray_start_global + (1.f + max_dist_from_camera) * mouse_direction; // add an epsilon to the sim version of the ray to avoid rounding problems.
*ray_end_region = regionp->getPosRegionFromGlobal( ray_end_global );
}
return TRUE;
}
S32 LLToolPlacer::getTreeGrassSpecies(std::map<std::string, S32> &table, const char *control, S32 max)
{
const std::string &species = gSavedSettings.getString(control);
std::map<std::string, S32>::iterator it;
it = table.find(species);
if (it != table.end()) {
return it->second;
} else {
// if saved species not found, default to "Random"
return (rand() % max);
}
}
BOOL LLToolPlacer::addObject( LLPCode pcode, S32 x, S32 y, U8 use_physics )
{
LLVector3 ray_start_region;
LLVector3 ray_end_region;
LLViewerRegion* regionp = NULL;
BOOL b_hit_land = FALSE;
S32 hit_face = -1;
LLViewerObject* hit_obj = NULL;
U8 state = 0;
BOOL success = raycastForNewObjPos( x, y, &hit_obj, &hit_face, &b_hit_land, &ray_start_region, &ray_end_region, &regionp );
if( !success )
{
return FALSE;
}
if( hit_obj && (hit_obj->isAvatar() || hit_obj->isAttachment()) )
{
// Can't create objects on avatars or attachments
return FALSE;
}
if (NULL == regionp)
{
llwarns << "regionp was NULL; aborting function." << llendl;
return FALSE;
}
if (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX)
{
LLFirstUse::useSandbox();
}
// Set params for new object based on its PCode.
LLQuaternion rotation;
LLVector3 scale = DEFAULT_OBJECT_SCALE;
U8 material = LL_MCODE_WOOD;
static LLCachedControl<bool> enable_BP("LiruEnableBuildPrefs", true);
static LLCachedControl<bool> duplicate("CreateToolCopySelection", true);
//If we are using the defaults, and we aren't duplicating
if(enable_BP && !duplicate)
{
scale = LLVector3(
gSavedSettings.getF32("BuildPrefs_Xsize"),
gSavedSettings.getF32("BuildPrefs_Ysize"),
gSavedSettings.getF32("BuildPrefs_Zsize"));
if(gSavedSettings.getString("BuildPrefs_Material")== "Stone") material = LL_MCODE_STONE;
else if(gSavedSettings.getString("BuildPrefs_Material")== "Metal") material = LL_MCODE_METAL;
//if(gSavedSettings.getString("BuildPrefs_Material")== "Wood") material = LL_MCODE_WOOD; redundant
else if(gSavedSettings.getString("BuildPrefs_Material")== "Flesh") material = LL_MCODE_FLESH;
else if(gSavedSettings.getString("BuildPrefs_Material")== "Rubber") material = LL_MCODE_RUBBER;
else if(gSavedSettings.getString("BuildPrefs_Material")== "Plastic") material = LL_MCODE_PLASTIC;
}
BOOL create_selected = FALSE;
LLVolumeParams volume_params;
switch (pcode)
{
case LL_PCODE_LEGACY_GRASS:
// Randomize size of grass patch
scale.setVec(10.f + ll_frand(20.f), 10.f + ll_frand(20.f), 1.f + ll_frand(2.f));
state = getTreeGrassSpecies(LLVOGrass::sSpeciesNames, "LastGrass", LLVOGrass::sMaxGrassSpecies);
break;
case LL_PCODE_LEGACY_TREE:
case LL_PCODE_TREE_NEW:
state = getTreeGrassSpecies(LLVOTree::sSpeciesNames, "LastTree", LLVOTree::sMaxTreeSpecies);
break;
case LL_PCODE_SPHERE:
case LL_PCODE_CONE:
case LL_PCODE_CUBE:
case LL_PCODE_CYLINDER:
case LL_PCODE_TORUS:
case LLViewerObject::LL_VO_SQUARE_TORUS:
case LLViewerObject::LL_VO_TRIANGLE_TORUS:
default:
create_selected = TRUE;
break;
}
// Play creation sound
if (gAudiop)
{
gAudiop->triggerSound( LLUUID(gSavedSettings.getString("UISndObjectCreate")),
gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
}
gMessageSystem->newMessageFast(_PREHASH_ObjectAdd);
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
// Alway rez objects as land group if available.
if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup"))
{
LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if(gAgent.isInGroup(parcel->getGroupID()))
gMessageSystem->addUUIDFast(_PREHASH_GroupID, parcel->getGroupID());
else if(gAgent.isInGroup(parcel->getOwnerID()))
gMessageSystem->addUUIDFast(_PREHASH_GroupID, parcel->getOwnerID());
else gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
} else gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
gMessageSystem->addU8Fast(_PREHASH_Material, material);
U32 flags = 0; // not selected
if (use_physics || (enable_BP && !duplicate && gSavedSettings.getBOOL("EmeraldBuildPrefs_Physical")))
{
flags |= FLAGS_USE_PHYSICS;
}
//if (create_selected)
// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0b) | Added: RLVa-1.0.0b
if ( (create_selected) && (!gRlvHandler.hasBehaviour(RLV_BHVR_EDIT)) )
// [/RLVa:KB]
{
flags |= FLAGS_CREATE_SELECTED;
}
gMessageSystem->addU32Fast(_PREHASH_AddFlags, flags );
LLPCode volume_pcode; // ...PCODE_VOLUME, or the original on error
switch (pcode)
{
case LL_PCODE_SPHERE:
rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1, 1 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_TORUS:
rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_CIRCLE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1.f, 0.25f ); // "top size"
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LLViewerObject::LL_VO_SQUARE_TORUS:
rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_CIRCLE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1.f, 0.25f ); // "top size"
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LLViewerObject::LL_VO_TRIANGLE_TORUS:
rotation.setQuat(90.f * DEG_TO_RAD, LLVector3::y_axis);
volume_params.setType( LL_PCODE_PROFILE_EQUALTRI, LL_PCODE_PATH_CIRCLE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1.f, 0.25f ); // "top size"
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_SPHERE_HEMI:
volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
//volume_params.setBeginAndEndS( 0.5f, 1.f );
volume_params.setBeginAndEndT( 0.f, 0.5f );
volume_params.setRatio ( 1, 1 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_CUBE:
volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1, 1 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_PRISM:
volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 0, 1 );
volume_params.setShear ( -0.5f, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_PYRAMID:
volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 0, 0 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_TETRAHEDRON:
volume_params.setType( LL_PCODE_PROFILE_EQUALTRI, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 0, 0 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_CYLINDER:
volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1, 1 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_CYLINDER_HEMI:
volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.25f, 0.75f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 1, 1 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_CONE:
volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.f, 1.f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 0, 0 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
case LL_PCODE_CONE_HEMI:
volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
volume_params.setBeginAndEndS( 0.25f, 0.75f );
volume_params.setBeginAndEndT( 0.f, 1.f );
volume_params.setRatio ( 0, 0 );
volume_params.setShear ( 0, 0 );
LLVolumeMessage::packVolumeParams(&volume_params, gMessageSystem);
volume_pcode = LL_PCODE_VOLUME;
break;
default:
LLVolumeMessage::packVolumeParams(0, gMessageSystem);
volume_pcode = pcode;
break;
}
gMessageSystem->addU8Fast(_PREHASH_PCode, volume_pcode);
gMessageSystem->addVector3Fast(_PREHASH_Scale, scale );
gMessageSystem->addQuatFast(_PREHASH_Rotation, rotation );
gMessageSystem->addVector3Fast(_PREHASH_RayStart, ray_start_region );
gMessageSystem->addVector3Fast(_PREHASH_RayEnd, ray_end_region );
gMessageSystem->addU8Fast(_PREHASH_BypassRaycast, (U8)b_hit_land );
gMessageSystem->addU8Fast(_PREHASH_RayEndIsIntersection, (U8)FALSE );
gMessageSystem->addU8Fast(_PREHASH_State, state);
// Limit raycast to a single object.
// Speeds up server raycast + avoid problems with server ray hitting objects
// that were clipped by the near plane or culled on the viewer.
LLUUID ray_target_id;
if( hit_obj )
{
ray_target_id = hit_obj->getID();
}
else
{
ray_target_id.setNull();
}
gMessageSystem->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
// Pack in name value pairs
gMessageSystem->sendReliable(regionp->getHost());
//If we are using the defaults, and we aren't duplicating
if(enable_BP && !duplicate) //then, actually call expectRez so that importtracker can do its thing, which sadly only works close up.
gImportTracker.expectRez();
// Spawns a message, so must be after above send
if (create_selected)
{
LLSelectMgr::getInstance()->deselectAll();
gViewerWindow->getWindow()->incBusyCount();
}
// VEFFECT: AddObject
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
effectp->setSourceObject((LLViewerObject*)gAgentAvatarp);
effectp->setPositionGlobal(regionp->getPosGlobalFromRegion(ray_end_region));
effectp->setDuration(LL_HUD_DUR_SHORT);
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CREATE_COUNT);
return TRUE;
}
// Used by the placer tool to add copies of the current selection.
// Inspired by add_object(). JC
BOOL LLToolPlacer::addDuplicate(S32 x, S32 y)
{
LLVector3 ray_start_region;
LLVector3 ray_end_region;
LLViewerRegion* regionp = NULL;
BOOL b_hit_land = FALSE;
S32 hit_face = -1;
LLViewerObject* hit_obj = NULL;
BOOL success = raycastForNewObjPos( x, y, &hit_obj, &hit_face, &b_hit_land, &ray_start_region, &ray_end_region, &regionp );
if( !success )
{
make_ui_sound("UISndInvalidOp");
return FALSE;
}
if( hit_obj && (hit_obj->isAvatar() || hit_obj->isAttachment()) )
{
// Can't create objects on avatars or attachments
make_ui_sound("UISndInvalidOp");
return FALSE;
}
// Limit raycast to a single object.
// Speeds up server raycast + avoid problems with server ray hitting objects
// that were clipped by the near plane or culled on the viewer.
LLUUID ray_target_id;
if( hit_obj )
{
ray_target_id = hit_obj->getID();
}
else
{
ray_target_id.setNull();
}
LLSelectMgr::getInstance()->selectDuplicateOnRay(ray_start_region,
ray_end_region,
b_hit_land, // suppress raycast
FALSE, // intersection
ray_target_id,
gSavedSettings.getBOOL("CreateToolCopyCenters"),
gSavedSettings.getBOOL("CreateToolCopyRotates"),
FALSE); // select copy
if (regionp
&& (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX))
{
LLFirstUse::useSandbox();
}
return TRUE;
}
BOOL LLToolPlacer::placeObject(S32 x, S32 y, MASK mask)
{
BOOL added = TRUE;
// [RLVa:KB] - Checked: 2010-01-02 (RLVa-1.1.0l) | Modified: RLVa-1.1.0l
if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_REZ)) || (gRlvHandler.hasBehaviour(RLV_BHVR_INTERACT))) )
{
return TRUE; // Callers seem to expect a "did you handle it?" so we return TRUE rather than FALSE
}
// [/RLVa:KB]
if (gSavedSettings.getBOOL("CreateToolCopySelection"))
{
added = addDuplicate(x, y);
}
else
{
added = addObject( sObjectType, x, y, FALSE );
}
// ...and go back to the default tool
if (added && !gSavedSettings.getBOOL("CreateToolKeepSelected"))
{
LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolCompTranslate::getInstance() );
}
return added;
}
BOOL LLToolPlacer::handleHover(S32 x, S32 y, MASK mask)
{
lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPlacer" << llendl;
gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLCREATE);
return TRUE;
}
void LLToolPlacer::handleSelect()
{
gFloaterTools->setStatusText("place");
}
void LLToolPlacer::handleDeselect()
{
}
//////////////////////////////////////////////////////
// LLToolPlacerPanel
S32 LLToolPlacerPanel::sButtonsAdded = 0;
LLButton* LLToolPlacerPanel::sButtons[ TOOL_PLACER_NUM_BUTTONS ];
LLToolPlacerPanel::LLToolPlacerPanel(const std::string& name, const LLRect& rect)
:
LLPanel( name, rect )
{
}
void LLToolPlacerPanel::addButton( const std::string& up_state, const std::string& down_state, LLPCode* pcode )
{
const S32 TOOL_SIZE = 32;
const S32 HORIZ_SPACING = TOOL_SIZE + 5;
const S32 VERT_SPACING = TOOL_SIZE + 5;
const S32 VPAD = 10;
const S32 HPAD = 7;
S32 row = sButtonsAdded / 4;
S32 column = sButtonsAdded % 4;
LLRect help_rect = gSavedSettings.getRect("ToolHelpRect");
// Build the rectangle, recalling the origin is at lower left
// and we want the icons to build down from the top.
LLRect rect;
rect.setLeftTopAndSize(
HPAD + (column * HORIZ_SPACING),
help_rect.mBottom - VPAD - (row * VERT_SPACING),
TOOL_SIZE,
TOOL_SIZE );
LLButton* btn = new LLButton(
std::string("ToolPlacerOptBtn"),
rect,
up_state,
down_state,
LLStringUtil::null, &LLToolPlacerPanel::setObjectType,
pcode,
LLFontGL::getFontSansSerif());
btn->setFollowsBottom();
btn->setFollowsLeft();
addChild(btn);
sButtons[sButtonsAdded] = btn;
sButtonsAdded++;
}
// static
void LLToolPlacerPanel::setObjectType( void* data )
{
LLPCode pcode = *(LLPCode*) data;
LLToolPlacer::setObjectType( pcode );
}