PageRenderTime 192ms CodeModel.GetById 40ms app.highlight 114ms RepoModel.GetById 17ms app.codeStats 1ms

/indra/newview/lltoolgrab.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1142 lines | 782 code | 196 blank | 164 comment | 129 complexity | 21898364ba954ed73320744ec1b0c43f MD5 | raw file
   1/** 
   2 * @file lltoolgrab.cpp
   3 * @brief LLToolGrab class implementation
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "lltoolgrab.h"
  30 
  31// library headers
  32#include "indra_constants.h"		// for agent control flags
  33#include "llviewercontrol.h"
  34#include "llquaternion.h"
  35#include "llbox.h"
  36#include "message.h"
  37#include "llview.h"
  38#include "llfontgl.h"
  39#include "llui.h"
  40
  41// newview headers
  42#include "llagent.h"
  43#include "llagentcamera.h"
  44#include "lldrawable.h"
  45#include "llfloatertools.h"
  46#include "llhudeffect.h"
  47#include "llhudmanager.h"
  48#include "llregionposition.h"
  49#include "llselectmgr.h"
  50#include "llstatusbar.h"
  51#include "lltoolmgr.h"
  52#include "lltoolpie.h"
  53#include "llviewercamera.h"
  54#include "llviewerobject.h"
  55#include "llviewerobjectlist.h" 
  56#include "llviewerregion.h"
  57#include "llvoavatarself.h"
  58#include "llworld.h"
  59
  60const S32 SLOP_DIST_SQ = 4;
  61
  62// Override modifier key behavior with these buttons
  63BOOL gGrabBtnVertical = FALSE;
  64BOOL gGrabBtnSpin = FALSE;
  65LLTool* gGrabTransientTool = NULL;
  66extern BOOL gDebugClicks;
  67
  68//
  69// Methods
  70//
  71LLToolGrab::LLToolGrab( LLToolComposite* composite )
  72:	LLTool( std::string("Grab"), composite ),
  73	mMode( GRAB_INACTIVE ),
  74	mVerticalDragging( FALSE ),
  75	mHitLand(FALSE),
  76	mLastMouseX(0),
  77	mLastMouseY(0),
  78	mAccumDeltaX(0),
  79	mAccumDeltaY(0),	
  80	mHasMoved( FALSE ),
  81	mOutsideSlop(FALSE),
  82	mDeselectedThisClick(FALSE),
  83	mLastFace(0),
  84	mSpinGrabbing( FALSE ),
  85	mSpinRotation(),
  86	mHideBuildHighlight(FALSE)
  87{ }
  88
  89LLToolGrab::~LLToolGrab()
  90{ }
  91
  92
  93// virtual
  94void LLToolGrab::handleSelect()
  95{
  96	if(gFloaterTools)
  97	{
  98		// viewer can crash during startup if we don't check.
  99		gFloaterTools->setStatusText("grab");
 100	}
 101	gGrabBtnVertical = FALSE;
 102	gGrabBtnSpin = FALSE;
 103}
 104
 105void LLToolGrab::handleDeselect()
 106{
 107	if( hasMouseCapture() )
 108	{
 109		setMouseCapture( FALSE );
 110	}
 111
 112}
 113
 114BOOL LLToolGrab::handleDoubleClick(S32 x, S32 y, MASK mask)
 115{
 116	if (gDebugClicks)
 117	{
 118		llinfos << "LLToolGrab handleDoubleClick (becoming mouseDown)" << llendl;
 119	}
 120
 121	return FALSE;
 122}
 123
 124BOOL LLToolGrab::handleMouseDown(S32 x, S32 y, MASK mask)
 125{
 126	if (gDebugClicks)
 127	{
 128		llinfos << "LLToolGrab handleMouseDown" << llendl;
 129	}
 130
 131	// call the base class to propogate info to sim
 132	LLTool::handleMouseDown(x, y, mask);
 133	
 134	if (!gAgent.leftButtonGrabbed())
 135	{
 136		// can grab transparent objects (how touch event propagates, scripters rely on this)
 137		gViewerWindow->pickAsync(x, y, mask, pickCallback, TRUE);
 138	}
 139	return TRUE;
 140}
 141
 142void LLToolGrab::pickCallback(const LLPickInfo& pick_info)
 143{
 144	LLToolGrab::getInstance()->mGrabPick = pick_info;
 145	LLViewerObject	*objectp = pick_info.getObject();
 146
 147	BOOL extend_select = (pick_info.mKeyMask & MASK_SHIFT);
 148
 149	if (!extend_select && !LLSelectMgr::getInstance()->getSelection()->isEmpty())
 150	{
 151		LLSelectMgr::getInstance()->deselectAll();
 152		LLToolGrab::getInstance()->mDeselectedThisClick = TRUE;
 153	}
 154	else
 155	{
 156		LLToolGrab::getInstance()->mDeselectedThisClick = FALSE;
 157	}
 158
 159	// if not over object, do nothing
 160	if (!objectp)
 161	{
 162		LLToolGrab::getInstance()->setMouseCapture(TRUE);
 163		LLToolGrab::getInstance()->mMode = GRAB_NOOBJECT;
 164		LLToolGrab::getInstance()->mGrabPick.mObjectID.setNull();
 165	}
 166	else
 167	{
 168		LLToolGrab::getInstance()->handleObjectHit(LLToolGrab::getInstance()->mGrabPick);
 169	}
 170}
 171
 172BOOL LLToolGrab::handleObjectHit(const LLPickInfo& info)
 173{
 174	mGrabPick = info;
 175	LLViewerObject* objectp = mGrabPick.getObject();
 176
 177	if (gDebugClicks)
 178	{
 179		llinfos << "LLToolGrab handleObjectHit " << info.mMousePt.mX << "," << info.mMousePt.mY << llendl;
 180	}
 181
 182	if (NULL == objectp) // unexpected
 183	{
 184		llwarns << "objectp was NULL; returning FALSE" << llendl;
 185		return FALSE;
 186	}
 187
 188	if (objectp->isAvatar())
 189	{
 190		if (gGrabTransientTool)
 191		{
 192			gBasicToolset->selectTool( gGrabTransientTool );
 193			gGrabTransientTool = NULL;
 194		}
 195		return TRUE;
 196	}
 197
 198	setMouseCapture( TRUE );
 199
 200	// Grabs always start from the root
 201	// objectp = (LLViewerObject *)objectp->getRoot();
 202
 203	LLViewerObject* parent = objectp->getRootEdit();
 204	BOOL script_touch = (objectp->flagHandleTouch()) || (parent && parent->flagHandleTouch());
 205
 206	// Clicks on scripted or physical objects are temporary grabs, so
 207	// not "Build mode"
 208	mHideBuildHighlight = script_touch || objectp->usePhysics();
 209
 210	if (!objectp->usePhysics())
 211	{
 212		if (script_touch)
 213		{
 214			mMode = GRAB_NONPHYSICAL;  // if it has a script, use the non-physical grab
 215		}
 216		else
 217		{
 218			// In mouselook, we shouldn't be able to grab non-physical, 
 219			// non-touchable objects.  If it has a touch handler, we
 220			// do grab it (so llDetectedGrab works), but movement is
 221			// blocked on the server side. JC
 222			if (gAgentCamera.cameraMouselook())
 223			{
 224				mMode = GRAB_LOCKED;
 225			}
 226			else
 227			{
 228				mMode = GRAB_ACTIVE_CENTER;
 229			}
 230
 231			gViewerWindow->hideCursor();
 232			gViewerWindow->moveCursorToCenter();
 233			
 234		}
 235	}
 236	else if( !objectp->permMove() )
 237	{
 238		// if mouse is over a physical object without move permission, show feedback if user tries to move it.
 239		mMode = GRAB_LOCKED;
 240
 241		// Don't bail out here, go on and grab so buttons can get
 242		// their "touched" event.
 243	}
 244	else
 245	{
 246		// if mouse is over a physical object with move permission, 
 247		// select it and enter "grab" mode (hiding cursor, etc.)
 248
 249		mMode = GRAB_ACTIVE_CENTER;
 250
 251		gViewerWindow->hideCursor();
 252		gViewerWindow->moveCursorToCenter();
 253	}
 254
 255	// Always send "touched" message
 256
 257	mLastMouseX = gViewerWindow->getCurrentMouseX();
 258	mLastMouseY = gViewerWindow->getCurrentMouseY();
 259	mAccumDeltaX = 0;
 260	mAccumDeltaY = 0;
 261	mHasMoved = FALSE;
 262	mOutsideSlop = FALSE;
 263
 264	mVerticalDragging = (info.mKeyMask == MASK_VERTICAL) || gGrabBtnVertical;
 265
 266	startGrab();
 267
 268	if ((info.mKeyMask == MASK_SPIN) || gGrabBtnSpin)
 269	{
 270		startSpin();
 271	}
 272
 273	LLSelectMgr::getInstance()->updateSelectionCenter();		// update selection beam
 274
 275	// update point at
 276	LLViewerObject *edit_object = info.getObject();
 277	if (edit_object && info.mPickType != LLPickInfo::PICK_FLORA)
 278	{
 279		LLVector3 local_edit_point = gAgent.getPosAgentFromGlobal(info.mPosGlobal);
 280		local_edit_point -= edit_object->getPositionAgent();
 281		local_edit_point = local_edit_point * ~edit_object->getRenderRotation();
 282		gAgentCamera.setPointAt(POINTAT_TARGET_GRAB, edit_object, local_edit_point );
 283		gAgentCamera.setLookAt(LOOKAT_TARGET_SELECT, edit_object, local_edit_point );
 284	}
 285
 286	// on transient grabs (clicks on world objects), kill the grab immediately
 287	if (!gViewerWindow->getLeftMouseDown() 
 288		&& gGrabTransientTool 
 289		&& (mMode == GRAB_NONPHYSICAL || mMode == GRAB_LOCKED))
 290	{
 291		gBasicToolset->selectTool( gGrabTransientTool );
 292		gGrabTransientTool = NULL;
 293	}
 294
 295	return TRUE;
 296}
 297
 298
 299void LLToolGrab::startSpin()
 300{
 301	LLViewerObject* objectp = mGrabPick.getObject();
 302	if (!objectp)
 303	{
 304		return;
 305	}
 306	mSpinGrabbing = TRUE;
 307
 308	// Was saveSelectedObjectTransform()
 309	LLViewerObject *root = (LLViewerObject *)objectp->getRoot();
 310	mSpinRotation = root->getRotation();
 311
 312	LLMessageSystem *msg = gMessageSystem;
 313	msg->newMessageFast(_PREHASH_ObjectSpinStart);
 314	msg->nextBlockFast(_PREHASH_AgentData);
 315	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 316	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 317	msg->nextBlockFast(_PREHASH_ObjectData);
 318	msg->addUUIDFast(_PREHASH_ObjectID, mGrabPick.mObjectID );
 319	msg->sendMessage( objectp->getRegion()->getHost() );
 320}
 321
 322
 323void LLToolGrab::stopSpin()
 324{
 325	mSpinGrabbing = FALSE;
 326
 327	LLViewerObject* objectp = mGrabPick.getObject();
 328	if (!objectp)
 329	{
 330		return;
 331	}
 332
 333	LLMessageSystem *msg = gMessageSystem;
 334	switch(mMode)
 335	{
 336	case GRAB_ACTIVE_CENTER:
 337	case GRAB_NONPHYSICAL:
 338	case GRAB_LOCKED:
 339		msg->newMessageFast(_PREHASH_ObjectSpinStop);
 340		msg->nextBlockFast(_PREHASH_AgentData);
 341		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 342		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 343		msg->nextBlockFast(_PREHASH_ObjectData);
 344		msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
 345		msg->sendMessage( objectp->getRegion()->getHost() );
 346		break;
 347
 348	case GRAB_NOOBJECT:
 349	case GRAB_INACTIVE:
 350	default:
 351		// do nothing
 352		break;
 353	}
 354}
 355
 356
 357void LLToolGrab::startGrab()
 358{
 359	// Compute grab_offset in the OBJECT's root's coordinate frame
 360	// (sometimes root == object)
 361	LLViewerObject* objectp = mGrabPick.getObject();
 362	if (!objectp)
 363	{
 364		return;
 365	}
 366
 367	LLViewerObject *root = (LLViewerObject *)objectp->getRoot();
 368
 369	// drag from center
 370	LLVector3d grab_start_global = root->getPositionGlobal();
 371
 372	// Where the grab starts, relative to the center of the root object of the set.
 373	// JC - This code looks wonky, but I believe it does the right thing.
 374	// Otherwise, when you grab a linked object set, it "pops" on the start
 375	// of the drag.
 376	LLVector3d grab_offsetd = root->getPositionGlobal() - objectp->getPositionGlobal();
 377
 378	LLVector3 grab_offset;
 379	grab_offset.setVec(grab_offsetd);
 380
 381	LLQuaternion rotation = root->getRotation();
 382	rotation.conjQuat();
 383	grab_offset = grab_offset * rotation;
 384
 385	// This planar drag starts at the grab point
 386	mDragStartPointGlobal = grab_start_global;
 387	mDragStartFromCamera = grab_start_global - gAgentCamera.getCameraPositionGlobal();
 388
 389	send_ObjectGrab_message(objectp, mGrabPick, grab_offset);
 390
 391	mGrabOffsetFromCenterInitial = grab_offset;
 392	mGrabHiddenOffsetFromCamera = mDragStartFromCamera;
 393
 394	mGrabTimer.reset();
 395
 396	mLastUVCoords = mGrabPick.mUVCoords;
 397	mLastSTCoords = mGrabPick.mSTCoords;
 398	mLastFace = mGrabPick.mObjectFace;
 399	mLastIntersection = mGrabPick.mIntersection;
 400	mLastNormal = mGrabPick.mNormal;
 401	mLastBinormal = mGrabPick.mBinormal;
 402	mLastGrabPos = LLVector3(-1.f, -1.f, -1.f);
 403}
 404
 405
 406BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask)
 407{
 408	if (!gViewerWindow->getLeftMouseDown())
 409	{
 410		gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
 411		setMouseCapture(FALSE);
 412		return TRUE;
 413	}
 414
 415	// Do the right hover based on mode
 416	switch( mMode )
 417	{
 418	case GRAB_ACTIVE_CENTER:
 419		handleHoverActive( x, y, mask );	// cursor hidden
 420		break;
 421		
 422	case GRAB_NONPHYSICAL:
 423		handleHoverNonPhysical(x, y, mask);
 424		break;
 425
 426	case GRAB_INACTIVE:
 427		handleHoverInactive( x, y, mask );  // cursor set here
 428		break;
 429
 430	case GRAB_NOOBJECT:
 431	case GRAB_LOCKED:
 432		handleHoverFailed( x, y, mask );
 433		break;
 434
 435	}
 436
 437	mLastMouseX = x;
 438	mLastMouseY = y;
 439	
 440	return TRUE;
 441}
 442
 443const F32 GRAB_SENSITIVITY_X = 0.0075f;
 444const F32 GRAB_SENSITIVITY_Y = 0.0075f;
 445
 446
 447
 448		
 449// Dragging.
 450void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 451{
 452	LLViewerObject* objectp = mGrabPick.getObject();
 453	if (!objectp || !hasMouseCapture() ) return;
 454	if (objectp->isDead())
 455	{
 456		// Bail out of drag because object has been killed
 457		setMouseCapture(FALSE);
 458		return;
 459	}
 460
 461	//--------------------------------------------------
 462	// Toggle spinning
 463	//--------------------------------------------------
 464	if (mSpinGrabbing && !(mask == MASK_SPIN) && !gGrabBtnSpin)
 465	{
 466		// user released ALT key, stop spinning
 467		stopSpin();
 468	}
 469	else if (!mSpinGrabbing && (mask == MASK_SPIN) )
 470	{
 471		// user pressed ALT key, start spinning
 472		startSpin();
 473	}
 474
 475	//--------------------------------------------------
 476	// Toggle vertical dragging
 477	//--------------------------------------------------
 478	if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical)
 479	{
 480		// ...switch to horizontal dragging
 481		mVerticalDragging = FALSE;
 482
 483		mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, objectp);
 484		mDragStartFromCamera = mDragStartPointGlobal - gAgentCamera.getCameraPositionGlobal();
 485	}
 486	else if (!mVerticalDragging && (mask == MASK_VERTICAL) )
 487	{
 488		// ...switch to vertical dragging
 489		mVerticalDragging = TRUE;
 490
 491		mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, objectp);
 492		mDragStartFromCamera = mDragStartPointGlobal - gAgentCamera.getCameraPositionGlobal();
 493	}
 494
 495	const F32 RADIANS_PER_PIXEL_X = 0.01f;
 496	const F32 RADIANS_PER_PIXEL_Y = 0.01f;
 497
 498	S32 dx = x - (gViewerWindow->getWorldViewWidthScaled() / 2);
 499	S32 dy = y - (gViewerWindow->getWorldViewHeightScaled() / 2);
 500
 501	if (dx != 0 || dy != 0)
 502	{
 503		mAccumDeltaX += dx;
 504		mAccumDeltaY += dy;
 505		S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY;
 506		if (dist_sq > SLOP_DIST_SQ)
 507		{
 508			mOutsideSlop = TRUE;
 509		}
 510
 511		// mouse has moved outside center
 512		mHasMoved = TRUE;
 513		
 514		if (mSpinGrabbing)
 515		{
 516			//------------------------------------------------------
 517			// Handle spinning
 518			//------------------------------------------------------
 519
 520			// x motion maps to rotation around vertical axis
 521			LLVector3 up(0.f, 0.f, 1.f);
 522			LLQuaternion rotation_around_vertical( dx*RADIANS_PER_PIXEL_X, up );
 523
 524			// y motion maps to rotation around left axis
 525			const LLVector3 &agent_left = LLViewerCamera::getInstance()->getLeftAxis();
 526			LLQuaternion rotation_around_left( dy*RADIANS_PER_PIXEL_Y, agent_left );
 527
 528			// compose with current rotation
 529			mSpinRotation = mSpinRotation * rotation_around_vertical;
 530			mSpinRotation = mSpinRotation * rotation_around_left;
 531
 532			// TODO: Throttle these
 533			LLMessageSystem *msg = gMessageSystem;
 534			msg->newMessageFast(_PREHASH_ObjectSpinUpdate);
 535			msg->nextBlockFast(_PREHASH_AgentData);
 536			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 537			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 538			msg->nextBlockFast(_PREHASH_ObjectData);
 539			msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
 540			msg->addQuatFast(_PREHASH_Rotation, mSpinRotation );
 541			msg->sendMessage( objectp->getRegion()->getHost() );
 542		}
 543		else
 544		{
 545			//------------------------------------------------------
 546			// Handle grabbing
 547			//------------------------------------------------------
 548
 549			LLVector3d x_part;
 550			x_part.setVec(LLViewerCamera::getInstance()->getLeftAxis());
 551			x_part.mdV[VZ] = 0.0;
 552			x_part.normVec();
 553
 554			LLVector3d y_part;
 555			if( mVerticalDragging )
 556			{
 557				y_part.setVec(LLViewerCamera::getInstance()->getUpAxis());
 558				// y_part.setVec(0.f, 0.f, 1.f);
 559			}
 560			else
 561			{
 562				// drag toward camera
 563				y_part = x_part % LLVector3d::z_axis;
 564				y_part.mdV[VZ] = 0.0;
 565				y_part.normVec();
 566			}
 567
 568			mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera 
 569				+ (x_part * (-dx * GRAB_SENSITIVITY_X)) 
 570				+ (y_part * ( dy * GRAB_SENSITIVITY_Y));
 571
 572
 573			// Send the message to the viewer.
 574			F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
 575			U32 dt_milliseconds = (U32) (1000.f * dt);
 576
 577			// need to return offset from mGrabStartPoint
 578			LLVector3d grab_point_global;
 579
 580			grab_point_global = gAgentCamera.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
 581
 582			/* Snap to grid disabled for grab tool - very confusing
 583			// Handle snapping to grid, but only when the tool is formally selected.
 584			BOOL snap_on = gSavedSettings.getBOOL("SnapEnabled");
 585			if (snap_on && !gGrabTransientTool)
 586			{
 587				F64	snap_size = gSavedSettings.getF32("GridResolution");
 588				U8 snap_dimensions = (mVerticalDragging ? 3 : 2);
 589
 590				for (U8 i = 0; i < snap_dimensions; i++)
 591				{
 592					grab_point_global.mdV[i] += snap_size / 2;
 593					grab_point_global.mdV[i] -= fmod(grab_point_global.mdV[i], snap_size);
 594				}
 595			}
 596			*/
 597
 598			// Don't let object centers go underground.
 599			F32 land_height = LLWorld::getInstance()->resolveLandHeightGlobal(grab_point_global);
 600
 601			if (grab_point_global.mdV[VZ] < land_height)
 602			{
 603				grab_point_global.mdV[VZ] = land_height;
 604			}
 605
 606			// For safety, cap heights where objects can be dragged
 607			if (grab_point_global.mdV[VZ] > MAX_OBJECT_Z)
 608			{
 609				grab_point_global.mdV[VZ] = MAX_OBJECT_Z;
 610			}
 611
 612			grab_point_global = LLWorld::getInstance()->clipToVisibleRegions(mDragStartPointGlobal, grab_point_global);
 613			// propagate constrained grab point back to grab offset
 614			mGrabHiddenOffsetFromCamera = grab_point_global - gAgentCamera.getCameraPositionGlobal();
 615
 616			// Handle auto-rotation at screen edge.
 617			LLVector3 grab_pos_agent = gAgent.getPosAgentFromGlobal( grab_point_global );
 618
 619			LLCoordGL grab_center_gl( gViewerWindow->getWorldViewWidthScaled() / 2, gViewerWindow->getWorldViewHeightScaled() / 2);
 620			LLViewerCamera::getInstance()->projectPosAgentToScreen(grab_pos_agent, grab_center_gl);
 621
 622			const S32 ROTATE_H_MARGIN = gViewerWindow->getWorldViewWidthScaled() / 20;
 623			const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
 624			const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
 625			// ...build mode moves camera about focus point
 626			if (grab_center_gl.mX < ROTATE_H_MARGIN)
 627			{
 628				if (gAgentCamera.getFocusOnAvatar())
 629				{
 630					gAgent.yaw(rotate_angle);
 631				}
 632				else
 633				{
 634					gAgentCamera.cameraOrbitAround(rotate_angle);
 635				}
 636			}
 637			else if (grab_center_gl.mX > gViewerWindow->getWorldViewWidthScaled() - ROTATE_H_MARGIN)
 638			{
 639				if (gAgentCamera.getFocusOnAvatar())
 640				{
 641					gAgent.yaw(-rotate_angle);
 642				}
 643				else
 644				{
 645					gAgentCamera.cameraOrbitAround(-rotate_angle);
 646				}
 647			}
 648
 649			// Don't move above top of screen or below bottom
 650			if ((grab_center_gl.mY < gViewerWindow->getWorldViewHeightScaled() - 6)
 651				&& (grab_center_gl.mY > 24))
 652			{
 653				// Transmit update to simulator
 654				LLVector3 grab_pos_region = objectp->getRegion()->getPosRegionFromGlobal( grab_point_global );
 655
 656				LLMessageSystem *msg = gMessageSystem;
 657				msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
 658				msg->nextBlockFast(_PREHASH_AgentData);
 659				msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 660				msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 661				msg->nextBlockFast(_PREHASH_ObjectData);
 662				msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
 663				msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial );
 664				msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region );
 665				msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds );
 666				msg->nextBlock("SurfaceInfo");
 667				msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords));
 668				msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords));
 669				msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace);
 670				msg->addVector3("Position", mGrabPick.mIntersection);
 671				msg->addVector3("Normal", mGrabPick.mNormal);
 672				msg->addVector3("Binormal", mGrabPick.mBinormal);
 673
 674				msg->sendMessage( objectp->getRegion()->getHost() );
 675			}
 676		}
 677
 678		gViewerWindow->moveCursorToCenter();
 679
 680		LLSelectMgr::getInstance()->updateSelectionCenter();
 681
 682	}
 683
 684	// once we've initiated a drag, lock the camera down
 685	if (mHasMoved)
 686	{
 687		if (!gAgentCamera.cameraMouselook() && 
 688			!objectp->isHUDAttachment() && 
 689			objectp->getRoot() == gAgentAvatarp->getRoot())
 690		{
 691			// force focus to point in space where we were looking previously
 692			gAgentCamera.setFocusGlobal(gAgentCamera.calcFocusPositionTargetGlobal(), LLUUID::null);
 693			gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE);
 694		}
 695		else
 696		{
 697			gAgentCamera.clearFocusObject();
 698		}
 699	}
 700
 701	// HACK to avoid assert: error checking system makes sure that the cursor is set during every handleHover.  This is actually a no-op since the cursor is hidden.
 702	gViewerWindow->setCursor(UI_CURSOR_ARROW);  
 703
 704	lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (active) [cursor hidden]" << llendl;		
 705}
 706 
 707
 708void LLToolGrab::handleHoverNonPhysical(S32 x, S32 y, MASK mask)
 709{
 710	LLViewerObject* objectp = mGrabPick.getObject();
 711	if (!objectp || !hasMouseCapture() ) return;
 712	if (objectp->isDead())
 713	{
 714		// Bail out of drag because object has been killed
 715		setMouseCapture(FALSE);
 716		return;
 717	}
 718
 719	LLPickInfo pick = mGrabPick;
 720	pick.mMousePt = LLCoordGL(x, y);
 721	pick.getSurfaceInfo();
 722
 723	// compute elapsed time
 724	F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
 725	U32 dt_milliseconds = (U32) (1000.f * dt);
 726
 727	// i'm not a big fan of the following code - it's been culled from the physical grab case.
 728	// ideally these two would be nicely integrated - but the code in that method is a serious
 729	// mess of spaghetti.  so here we go:
 730
 731	LLVector3 grab_pos_region(0,0,0);
 732	
 733	const BOOL SUPPORT_LLDETECTED_GRAB = TRUE;
 734	if (SUPPORT_LLDETECTED_GRAB)
 735	{
 736		//--------------------------------------------------
 737		// Toggle vertical dragging
 738		//--------------------------------------------------
 739		if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical)
 740		{
 741			mVerticalDragging = FALSE;
 742		}
 743	
 744		else if (!mVerticalDragging && (mask == MASK_VERTICAL) )
 745		{
 746			mVerticalDragging = TRUE;
 747		}
 748	
 749		S32 dx = x - mLastMouseX;
 750		S32 dy = y - mLastMouseY;
 751
 752		if (dx != 0 || dy != 0)
 753		{
 754			mAccumDeltaX += dx;
 755			mAccumDeltaY += dy;
 756		
 757			S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY;
 758			if (dist_sq > SLOP_DIST_SQ)
 759			{
 760				mOutsideSlop = TRUE;
 761			}
 762
 763			// mouse has moved 
 764			mHasMoved = TRUE;
 765
 766			//------------------------------------------------------
 767			// Handle grabbing
 768			//------------------------------------------------------
 769
 770			LLVector3d x_part;
 771			x_part.setVec(LLViewerCamera::getInstance()->getLeftAxis());
 772			x_part.mdV[VZ] = 0.0;
 773			x_part.normVec();
 774
 775			LLVector3d y_part;
 776			if( mVerticalDragging )
 777			{
 778				y_part.setVec(LLViewerCamera::getInstance()->getUpAxis());
 779				// y_part.setVec(0.f, 0.f, 1.f);
 780			}
 781			else
 782			{
 783				// drag toward camera
 784				y_part = x_part % LLVector3d::z_axis;
 785				y_part.mdV[VZ] = 0.0;
 786				y_part.normVec();
 787			}
 788
 789			mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera 
 790				+ (x_part * (-dx * GRAB_SENSITIVITY_X)) 
 791				+ (y_part * ( dy * GRAB_SENSITIVITY_Y));
 792
 793		}
 794		
 795		// need to return offset from mGrabStartPoint
 796		LLVector3d grab_point_global = gAgentCamera.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
 797		grab_pos_region = objectp->getRegion()->getPosRegionFromGlobal( grab_point_global );
 798	}
 799
 800
 801	// only send message if something has changed since last message
 802	
 803	BOOL changed_since_last_update = FALSE;
 804
 805	// test if touch data needs to be updated
 806	if ((pick.mObjectFace != mLastFace) ||
 807		(pick.mUVCoords != mLastUVCoords) ||
 808		(pick.mSTCoords != mLastSTCoords) ||
 809		(pick.mIntersection != mLastIntersection) ||
 810		(pick.mNormal != mLastNormal) ||
 811		(pick.mBinormal != mLastBinormal) ||
 812		(grab_pos_region != mLastGrabPos))
 813	{
 814		changed_since_last_update = TRUE;
 815	}
 816
 817	if (changed_since_last_update)
 818	{
 819		LLMessageSystem *msg = gMessageSystem;
 820		msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
 821		msg->nextBlockFast(_PREHASH_AgentData);
 822		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 823		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 824		msg->nextBlockFast(_PREHASH_ObjectData);
 825		msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
 826		msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial );
 827		msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region );
 828		msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds );
 829		msg->nextBlock("SurfaceInfo");
 830		msg->addVector3("UVCoord", LLVector3(pick.mUVCoords));
 831		msg->addVector3("STCoord", LLVector3(pick.mSTCoords));
 832		msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
 833		msg->addVector3("Position", pick.mIntersection);
 834		msg->addVector3("Normal", pick.mNormal);
 835		msg->addVector3("Binormal", pick.mBinormal);
 836
 837		msg->sendMessage( objectp->getRegion()->getHost() );
 838
 839		mLastUVCoords = pick.mUVCoords;
 840		mLastSTCoords = pick.mSTCoords;
 841		mLastFace = pick.mObjectFace;
 842		mLastIntersection = pick.mIntersection;
 843		mLastNormal= pick.mNormal;
 844		mLastBinormal= pick.mBinormal;
 845		mLastGrabPos = grab_pos_region;
 846	}
 847	
 848	// update point-at / look-at
 849	if (pick.mObjectFace != -1) // if the intersection was on the surface of the obejct
 850	{
 851		LLVector3 local_edit_point = pick.mIntersection;
 852		local_edit_point -= objectp->getPositionAgent();
 853		local_edit_point = local_edit_point * ~objectp->getRenderRotation();
 854		gAgentCamera.setPointAt(POINTAT_TARGET_GRAB, objectp, local_edit_point );
 855		gAgentCamera.setLookAt(LOOKAT_TARGET_SELECT, objectp, local_edit_point );
 856	}
 857	
 858	
 859	
 860	gViewerWindow->setCursor(UI_CURSOR_HAND);  
 861}
 862 
 863
 864// Not dragging.  Just showing affordances
 865void LLToolGrab::handleHoverInactive(S32 x, S32 y, MASK mask)
 866{
 867	// JC - TODO - change cursor based on gGrabBtnVertical, gGrabBtnSpin
 868	lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (inactive-not over editable object)" << llendl;		
 869	gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
 870}
 871
 872// User is trying to do something that's not allowed.
 873void LLToolGrab::handleHoverFailed(S32 x, S32 y, MASK mask)
 874{
 875	if( GRAB_NOOBJECT == mMode )
 876	{
 877		gViewerWindow->setCursor(UI_CURSOR_NO);
 878		lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (not on object)" << llendl;		
 879	}
 880	else
 881	{
 882		S32 dist_sq = (x-mGrabPick.mMousePt.mX) * (x-mGrabPick.mMousePt.mX) + (y-mGrabPick.mMousePt.mY) * (y-mGrabPick.mMousePt.mY);
 883		if( mOutsideSlop || dist_sq > SLOP_DIST_SQ )
 884		{
 885			mOutsideSlop = TRUE;
 886
 887			switch( mMode )
 888			{
 889			case GRAB_LOCKED:
 890				gViewerWindow->setCursor(UI_CURSOR_GRABLOCKED);
 891				lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, no move permission)" << llendl;		
 892				break;
 893
 894//  Non physical now handled by handleHoverActive - CRO				
 895//			case GRAB_NONPHYSICAL:
 896//				gViewerWindow->setCursor(UI_CURSOR_ARROW);
 897//				lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, nonphysical)" << llendl;		
 898//				break;
 899			default:
 900				llassert(0);
 901			}
 902		}
 903		else
 904		{
 905			gViewerWindow->setCursor(UI_CURSOR_ARROW);
 906			lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed but within slop)" << llendl;		
 907		}
 908	}
 909}
 910
 911
 912
 913
 914BOOL LLToolGrab::handleMouseUp(S32 x, S32 y, MASK mask)
 915{
 916	// call the base class to propogate info to sim
 917	LLTool::handleMouseUp(x, y, mask);
 918
 919	if( hasMouseCapture() )
 920	{
 921		setMouseCapture( FALSE );
 922	}
 923	mMode = GRAB_INACTIVE;
 924
 925	// HACK: Make some grabs temporary
 926	if (gGrabTransientTool)
 927	{
 928		gBasicToolset->selectTool( gGrabTransientTool );
 929		gGrabTransientTool = NULL;
 930	}
 931
 932	//gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
 933
 934	return TRUE;
 935} 
 936
 937void LLToolGrab::stopEditing()
 938{
 939	if( hasMouseCapture() )
 940	{
 941		setMouseCapture( FALSE );
 942	}
 943}
 944
 945void LLToolGrab::onMouseCaptureLost()
 946{
 947	LLViewerObject* objectp = mGrabPick.getObject();
 948	if (!objectp)
 949	{
 950		gViewerWindow->showCursor();
 951		return;
 952	}
 953	// First, fix cursor placement
 954	if( !gAgentCamera.cameraMouselook() 
 955		&& (GRAB_ACTIVE_CENTER == mMode))
 956	{
 957		if (objectp->isHUDAttachment())
 958		{
 959			// ...move cursor "naturally", as if it had moved when hidden
 960			S32 x = mGrabPick.mMousePt.mX + mAccumDeltaX;
 961			S32 y = mGrabPick.mMousePt.mY + mAccumDeltaY;
 962			LLUI::setMousePositionScreen(x, y);
 963		}
 964		else if (mHasMoved)
 965		{
 966			// ...move cursor back to the center of the object
 967			LLVector3 grab_point_agent = objectp->getRenderPosition();
 968
 969			LLCoordGL gl_point;
 970			if (LLViewerCamera::getInstance()->projectPosAgentToScreen(grab_point_agent, gl_point))
 971			{
 972				LLUI::setMousePositionScreen(gl_point.mX, gl_point.mY);
 973			}
 974		}
 975		else
 976		{
 977			// ...move cursor back to click position
 978			LLUI::setMousePositionScreen(mGrabPick.mMousePt.mX, mGrabPick.mMousePt.mY);
 979		}
 980
 981		gViewerWindow->showCursor();
 982	}
 983
 984	stopGrab();
 985	if (mSpinGrabbing)
 986	stopSpin();
 987	
 988	mMode = GRAB_INACTIVE;
 989
 990	mHideBuildHighlight = FALSE;
 991
 992	mGrabPick.mObjectID.setNull();
 993
 994	LLSelectMgr::getInstance()->updateSelectionCenter();
 995	gAgentCamera.setPointAt(POINTAT_TARGET_CLEAR);
 996	gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR);
 997
 998	dialog_refresh_all();
 999}
1000
1001
1002void LLToolGrab::stopGrab()
1003{
1004	LLViewerObject* objectp = mGrabPick.getObject();
1005	if (!objectp)
1006	{
1007		return;
1008	}
1009
1010	LLPickInfo pick = mGrabPick;
1011
1012	if (mMode == GRAB_NONPHYSICAL)
1013	{
1014		// for non-physical (touch) grabs,
1015		// gather surface info for this degrab (mouse-up)
1016		S32 x = gViewerWindow->getCurrentMouseX();
1017		S32 y = gViewerWindow->getCurrentMouseY();
1018		pick.mMousePt = LLCoordGL(x, y);
1019		pick.getSurfaceInfo();
1020	}
1021
1022	// Next, send messages to simulator
1023	switch(mMode)
1024	{
1025	case GRAB_ACTIVE_CENTER:
1026	case GRAB_NONPHYSICAL:
1027	case GRAB_LOCKED:
1028		send_ObjectDeGrab_message(objectp, pick);
1029		mVerticalDragging = FALSE;
1030		break;
1031
1032	case GRAB_NOOBJECT:
1033	case GRAB_INACTIVE:
1034	default:
1035		// do nothing
1036		break;
1037	}
1038
1039	mHideBuildHighlight = FALSE;
1040}
1041
1042
1043void LLToolGrab::draw()
1044{ }
1045
1046void LLToolGrab::render()
1047{ }
1048
1049BOOL LLToolGrab::isEditing()
1050{
1051	return (mGrabPick.getObject().notNull());
1052}
1053
1054LLViewerObject* LLToolGrab::getEditingObject()
1055{
1056	return mGrabPick.getObject();
1057}
1058
1059
1060LLVector3d LLToolGrab::getEditingPointGlobal()
1061{
1062	return getGrabPointGlobal();
1063}
1064
1065LLVector3d LLToolGrab::getGrabPointGlobal()
1066{
1067	switch(mMode)
1068	{
1069	case GRAB_ACTIVE_CENTER:
1070	case GRAB_NONPHYSICAL:
1071	case GRAB_LOCKED:
1072		return gAgentCamera.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
1073
1074	case GRAB_NOOBJECT:
1075	case GRAB_INACTIVE:
1076	default:
1077		return gAgent.getPositionGlobal();
1078	}
1079}
1080
1081
1082void send_ObjectGrab_message(LLViewerObject* object, const LLPickInfo & pick, const LLVector3 &grab_offset)
1083{
1084	if (!object) return;
1085
1086	LLMessageSystem	*msg = gMessageSystem;
1087
1088	msg->newMessageFast(_PREHASH_ObjectGrab);
1089	msg->nextBlockFast( _PREHASH_AgentData);
1090	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1091	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1092	msg->nextBlockFast( _PREHASH_ObjectData);
1093	msg->addU32Fast(    _PREHASH_LocalID, object->mLocalID);
1094	msg->addVector3Fast(_PREHASH_GrabOffset, grab_offset);
1095	msg->nextBlock("SurfaceInfo");
1096	msg->addVector3("UVCoord", LLVector3(pick.mUVCoords));
1097	msg->addVector3("STCoord", LLVector3(pick.mSTCoords));
1098	msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
1099	msg->addVector3("Position", pick.mIntersection);
1100	msg->addVector3("Normal", pick.mNormal);
1101	msg->addVector3("Binormal", pick.mBinormal);
1102	msg->sendMessage( object->getRegion()->getHost());
1103
1104	/*  Diagnostic code
1105	llinfos << "mUVCoords: " << pick.mUVCoords
1106			<< ", mSTCoords: " << pick.mSTCoords
1107			<< ", mObjectFace: " << pick.mObjectFace
1108			<< ", mIntersection: " << pick.mIntersection
1109			<< ", mNormal: " << pick.mNormal
1110			<< ", mBinormal: " << pick.mBinormal
1111			<< llendl;
1112
1113	llinfos << "Avatar pos: " << gAgent.getPositionAgent() << llendl;
1114	llinfos << "Object pos: " << object->getPosition() << llendl;
1115	*/
1116}
1117
1118
1119void send_ObjectDeGrab_message(LLViewerObject* object, const LLPickInfo & pick)
1120{
1121	if (!object) return;
1122
1123	LLMessageSystem	*msg = gMessageSystem;
1124
1125	msg->newMessageFast(_PREHASH_ObjectDeGrab);
1126	msg->nextBlockFast(_PREHASH_AgentData);
1127	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1128	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1129	msg->nextBlockFast(_PREHASH_ObjectData);
1130	msg->addU32Fast(_PREHASH_LocalID, object->mLocalID);
1131	msg->nextBlock("SurfaceInfo");
1132	msg->addVector3("UVCoord", LLVector3(pick.mUVCoords));
1133	msg->addVector3("STCoord", LLVector3(pick.mSTCoords));
1134	msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
1135	msg->addVector3("Position", pick.mIntersection);
1136	msg->addVector3("Normal", pick.mNormal);
1137	msg->addVector3("Binormal", pick.mBinormal);
1138	msg->sendMessage(object->getRegion()->getHost());
1139}
1140
1141
1142