Vadim ProductEngine avatar Vadim ProductEngine committed ae53af3

STORM-1612 FIXED Implemented click-to-walk in Advanced mode.

Changes:
* Click and double click in-world can now trigger click-to-walk or
click-to-teleport actions, depending on preferences.
* Grouped keyboard- and mouse-related settings in Preferences -> Move.

Comments (0)

Files changed (6)

indra/newview/app_settings/settings.xml

       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
+      <integer>1</integer>
+    </map>
+    <key>ClickToTeleport</key>
+    <map>
+      <key>Comment</key>
+      <string>Click in world to teleport to location</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
       <integer>0</integer>
     </map>
     <key>ShowOfferedInventory</key>

indra/newview/llfloaterpreference.cpp

 	mOriginalIMViaEmail(false),
 	mLanguageChanged(false),
 	mAvatarDataInitialized(false),
-	mDoubleClickActionDirty(false)
+	mClickActionDirty(false)
 {
 	
 	//Build Floater is now Called from 	LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>);
 	
 	sSkin = gSavedSettings.getString("SkinCurrent");
 
-	mCommitCallbackRegistrar.add("Pref.CommitDoubleClickChekbox",	boost::bind(&LLFloaterPreference::onDoubleClickCheckBox, this, _1));
-	mCommitCallbackRegistrar.add("Pref.CommitRadioDoubleClick",	boost::bind(&LLFloaterPreference::onDoubleClickRadio, this));
+	mCommitCallbackRegistrar.add("Pref.CommitClickToWalkCheckbox",		boost::bind(&LLFloaterPreference::onWalkCheckboxCommit, this));
+	mCommitCallbackRegistrar.add("Pref.CommitClickToTeleportCheckbox",	boost::bind(&LLFloaterPreference::onTeleportCheckboxCommit, this));
+	mCommitCallbackRegistrar.add("Pref.CommitWalkTriggerRadio",			boost::bind(&LLFloaterPreference::onWalkTriggerRadioCommit, this));
+	mCommitCallbackRegistrar.add("Pref.CommitTeleportTriggerRadio",		boost::bind(&LLFloaterPreference::onTeleportTriggerRadioCommit, this));
 
 	gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));	
 	gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));	
 	if (!tabcontainer->selectTab(gSavedSettings.getS32("LastPrefTab")))
 		tabcontainer->selectFirstTab();
 
-	updateDoubleClickControls();
-
 	getChild<LLUICtrl>("cache_location")->setEnabled(FALSE); // make it read-only but selectable (STORM-227)
 	std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "");
 	setCacheLocation(cache_location);
 
 	saveAvatarProperties();
 
-	if (mDoubleClickActionDirty)
+	if (mClickActionDirty)
 	{
-		updateDoubleClickSettings();
-		mDoubleClickActionDirty = false;
+		updateClickActionSettings();
+		mClickActionDirty = false;
 	}
 }
 
 	// reverts any changes to current skin
 	gSavedSettings.setString("SkinCurrent", sSkin);
 
-	if (mDoubleClickActionDirty)
+	if (mClickActionDirty)
 	{
-		updateDoubleClickControls();
-		mDoubleClickActionDirty = false;
+		updateClickActionControls();
+		mClickActionDirty = false;
 	}
+
 	LLFloaterPreferenceProxy * advanced_proxy_settings = LLFloaterReg::findTypedInstance<LLFloaterPreferenceProxy>("prefs_proxy");
 	if (advanced_proxy_settings)
 	{
 
 	// Display selected maturity icons.
 	onChangeMaturity();
+
+	// Load (double-)click to walk/teleport settings.
+	updateClickActionControls();
 	
 	// Enabled/disabled popups, might have been changed by user actions
 	// while preferences floater was closed.
 	}
 }
 
-void LLFloaterPreference::onDoubleClickCheckBox(LLUICtrl* ctrl)
-{
-	if (!ctrl) return;
-	mDoubleClickActionDirty = true;
-	LLRadioGroup* radio_double_click_action = getChild<LLRadioGroup>("double_click_action");
-	if (!radio_double_click_action) return;
-	// select default value("teleport") in radio-group.
-	radio_double_click_action->setSelectedIndex(0);
-	// set radio-group enabled depending on state of checkbox
-	radio_double_click_action->setEnabled(ctrl->getValue());
-}
-
-void LLFloaterPreference::onDoubleClickRadio()
-{
-	mDoubleClickActionDirty = true;
-}
-
-void LLFloaterPreference::updateDoubleClickSettings()
-{
-	LLCheckBoxCtrl* double_click_action_cb = getChild<LLCheckBoxCtrl>("double_click_chkbox");
-	if (!double_click_action_cb) return;
-	bool enable = double_click_action_cb->getValue().asBoolean();
-
-	LLRadioGroup* radio_double_click_action = getChild<LLRadioGroup>("double_click_action");
-	if (!radio_double_click_action) return;
-	
-	// enable double click radio-group depending on state of checkbox
-	radio_double_click_action->setEnabled(enable);
-	
-	if (!enable)
-	{
-		// set double click action settings values to false if checkbox was unchecked
-		gSavedSettings.setBOOL("DoubleClickAutoPilot", false);
-		gSavedSettings.setBOOL("DoubleClickTeleport", false);
-	}
-	else
-	{
-		std::string selected = radio_double_click_action->getValue().asString();
-		bool teleport_selected = selected == "radio_teleport";
-		// set double click action settings values depending on chosen radio-button
-		gSavedSettings.setBOOL( "DoubleClickTeleport", teleport_selected );
-		gSavedSettings.setBOOL( "DoubleClickAutoPilot", !teleport_selected );
-	}
-}
-
 void LLFloaterPreference::onClickProxySettings()
 {
 	LLFloaterReg::showInstance("prefs_proxy");
 }
 
-void LLFloaterPreference::updateDoubleClickControls()
+void LLFloaterPreference::onWalkCheckboxCommit()
 {
-	// check is one of double-click actions settings enabled
-	bool double_click_action_enabled = gSavedSettings.getBOOL("DoubleClickAutoPilot") || gSavedSettings.getBOOL("DoubleClickTeleport");
-	LLCheckBoxCtrl* double_click_action_cb = getChild<LLCheckBoxCtrl>("double_click_chkbox");
-	if (double_click_action_cb)
+	LLCheckBoxCtrl* walk_trigger_cb		= getChild<LLCheckBoxCtrl>("walk_to_chkbox");
+	LLRadioGroup* walk_trigger_radio	= getChild<LLRadioGroup>("walk_trigger_radio");
+	const bool checked = walk_trigger_cb->getValue().asBoolean();
+
+	mClickActionDirty = true;
+	walk_trigger_radio->setEnabled(checked);
+	if (checked)
 	{
-		// check checkbox if one of double-click actions settings enabled, uncheck otherwise
-		double_click_action_cb->setValue(double_click_action_enabled);
+		fixWalkRadioValue(); // don't allow two actions on click or double click
 	}
-	LLRadioGroup* double_click_action_radio = getChild<LLRadioGroup>("double_click_action");
-	if (!double_click_action_radio) return;
-	// set radio-group enabled if one of double-click actions settings enabled
-	double_click_action_radio->setEnabled(double_click_action_enabled);
-	// select button in radio-group depending on setting
-	double_click_action_radio->setSelectedIndex(gSavedSettings.getBOOL("DoubleClickAutoPilot"));
+}
+
+void LLFloaterPreference::onTeleportCheckboxCommit()
+{
+	LLCheckBoxCtrl* teleport_trigger_cb		= getChild<LLCheckBoxCtrl>("teleport_to_chkbox");
+	LLRadioGroup* teleport_trigger_radio	= getChild<LLRadioGroup>("teleport_trigger_radio");
+	const bool checked = teleport_trigger_cb->getValue().asBoolean();
+
+	mClickActionDirty = true;
+	teleport_trigger_radio->setEnabled(checked);
+	if (checked)
+	{
+		fixTeleportRadioValue(); // don't allow two actions on click or double click
+	}
+}
+
+void LLFloaterPreference::onWalkTriggerRadioCommit()
+{
+	mClickActionDirty = true;
+	fixTeleportRadioValue();
+}
+
+void LLFloaterPreference::onTeleportTriggerRadioCommit()
+{
+	mClickActionDirty = true;
+	fixWalkRadioValue();
+}
+
+void LLFloaterPreference::fixWalkRadioValue()
+{
+	LLRadioGroup* walk_trigger_radio		= getChild<LLRadioGroup>("walk_trigger_radio");
+	LLRadioGroup* teleport_trigger_radio	= getChild<LLRadioGroup>("teleport_trigger_radio");
+
+	walk_trigger_radio->setSelectedIndex(!teleport_trigger_radio->getSelectedIndex());
+}
+
+
+void LLFloaterPreference::fixTeleportRadioValue()
+{
+	LLRadioGroup* walk_trigger_radio		= getChild<LLRadioGroup>("walk_trigger_radio");
+	LLRadioGroup* teleport_trigger_radio	= getChild<LLRadioGroup>("teleport_trigger_radio");
+
+	teleport_trigger_radio->setSelectedIndex(!walk_trigger_radio->getSelectedIndex());
+}
+
+void LLFloaterPreference::updateClickActionSettings()
+{
+	const bool walk_trigger_enabled = getChild<LLCheckBoxCtrl>("walk_to_chkbox")->getValue().asBoolean();
+	const bool teleport_trigger_enabled = getChild<LLCheckBoxCtrl>("teleport_to_chkbox")->getValue().asBoolean();
+
+	const bool walk_on_dbl_click = (bool) getChild<LLRadioGroup>("walk_trigger_radio")->getSelectedIndex();
+	const bool teleport_on_dbl_click = (bool) getChild<LLRadioGroup>("teleport_trigger_radio")->getSelectedIndex();
+
+	gSavedSettings.setBOOL("ClickToWalk", walk_trigger_enabled && !walk_on_dbl_click);
+	gSavedSettings.setBOOL("ClickToTeleport", teleport_trigger_enabled && !teleport_on_dbl_click);
+	gSavedSettings.setBOOL("DoubleClickAutoPilot", walk_trigger_enabled && walk_on_dbl_click);
+	gSavedSettings.setBOOL("DoubleClickTeleport", teleport_trigger_enabled && teleport_on_dbl_click);
+}
+
+void LLFloaterPreference::updateClickActionControls()
+{
+	LLCheckBoxCtrl* walk_trigger_cb		= getChild<LLCheckBoxCtrl>("walk_to_chkbox");
+	LLCheckBoxCtrl* teleport_trigger_cb	= getChild<LLCheckBoxCtrl>("teleport_to_chkbox");
+
+	LLRadioGroup* walk_trigger_radio		= getChild<LLRadioGroup>("walk_trigger_radio");
+	LLRadioGroup* teleport_trigger_radio	= getChild<LLRadioGroup>("teleport_trigger_radio");
+
+	const bool click_to_walk = gSavedSettings.getBOOL("ClickToWalk");
+	const bool click_to_teleport = gSavedSettings.getBOOL("ClickToTeleport");
+	const bool dbl_click_to_walk = gSavedSettings.getBOOL("DoubleClickAutoPilot");
+	const bool dbl_click_to_teleport = gSavedSettings.getBOOL("DoubleClickTeleport");
+
+	const bool walk_trigger_enabled = click_to_walk || dbl_click_to_walk;
+	const bool teleport_trigger_enabled = click_to_teleport || dbl_click_to_teleport;
+
+	walk_trigger_cb->setValue(walk_trigger_enabled);
+	teleport_trigger_cb->setValue(teleport_trigger_enabled);
+
+	walk_trigger_radio->setEnabled(walk_trigger_enabled);
+	walk_trigger_radio->setSelectedIndex(dbl_click_to_walk);
+
+	teleport_trigger_radio->setEnabled(teleport_trigger_enabled);
+	teleport_trigger_radio->setSelectedIndex(dbl_click_to_teleport);
+
+	// Make sure it doesn't look like there is more than one action per trigger.
+	if (teleport_trigger_enabled)
+	{
+		fixWalkRadioValue();
+	}
+	else
+	{
+		fixTeleportRadioValue();
+	}
 }
 
 void LLFloaterPreference::applyUIColor(LLUICtrl* ctrl, const LLSD& param)

indra/newview/llfloaterpreference.h

 	void setHardwareDefaults();
 	// callback for when client turns on shaders
 	void onVertexShaderEnable();
-	// callback for changing double click action checkbox
-	void onDoubleClickCheckBox(LLUICtrl* ctrl);
-	// callback for selecting double click action radio-button
-	void onDoubleClickRadio();
-	// updates double-click action settings depending on controls from preferences
-	void updateDoubleClickSettings();
-	// updates double-click action controls depending on values from settings.xml
-	void updateDoubleClickControls();
+
+	// callback for clicking the "Walk to Click Point" checkbox
+	void onWalkCheckboxCommit();
+	// callback for clicking the "Teleport to Click Point" checkbox
+	void onTeleportCheckboxCommit();
+	// callback for selecting trigger for "Walk to Click Point"
+	void onWalkTriggerRadioCommit();
+	// callback for selecting trigger for "Teleport to Click Point"
+	void onTeleportTriggerRadioCommit();
+	// make sure the radio buttons have mutually exclusive values
+	void fixWalkRadioValue();
+	// make sure the radio buttons have mutually exclusive values
+	void fixTeleportRadioValue();
+	// updates click/double-click action settings depending on controls values
+	void updateClickActionSettings();
+	// updates click/double-click action controls depending on values from settings.xml
+	void updateClickActionControls();
 	
 	// This function squirrels away the current values of the controls so that
 	// cancel() can restore them.	
 	static std::string sSkin;
 	// set true if state of double-click action checkbox or radio-group was changed by user
 	// (reset back to false on apply or cancel)
-	bool mDoubleClickActionDirty;
+	bool mClickActionDirty; ///< Set to true when the click/double-click options get changed by user.
 	bool mGotPersonalInfo;
 	bool mOriginalIMViaEmail;
 	bool mLanguageChanged;

indra/newview/lltoolpie.cpp

 #include "llagent.h"
 #include "llagentcamera.h"
 #include "llavatarnamecache.h"
-#include "llviewercontrol.h"
+#include "lleventtimer.h"
 #include "llfocusmgr.h"
 #include "llfirstuse.h"
 #include "llfloaterland.h"
 #include "lltrans.h"
 #include "llviewercamera.h"
 #include "llviewerparcelmedia.h"
+#include "llviewercontrol.h"
 #include "llviewermenu.h"
 #include "llviewerobjectlist.h"
 #include "llviewerobject.h"
 static void handle_click_action_open_media(LLPointer<LLViewerObject> objectp);
 static ECursorType cursor_from_parcel_media(U8 click_action);
 
+/**
+ * Schedule teleport to the specified location when user clicks in world.
+ *
+ * Deferring teleport is needed for double-click-to-walk to work.
+ * If double click in the world view occurs, teleport gets canceled.
+ */
+class LLClickToTeleportTimer : public LLEventTimer
+{
+	LOG_CLASS(LLClickToTeleportTimer);
+public:
+	LLClickToTeleportTimer(const LLVector3d& pos);
+	~LLClickToTeleportTimer();
+	/*virtual*/ BOOL tick();
+
+private:
+	LLVector3d mTeleportPos;
+};
+
+LLClickToTeleportTimer::LLClickToTeleportTimer(const LLVector3d& pos)
+:	LLEventTimer(0.33) // should be greater than double click interval
+,	mTeleportPos(pos)
+{
+};
+
+LLClickToTeleportTimer::~LLClickToTeleportTimer()
+{
+	LLToolPie::instance().mClickToTeleportTimer = NULL;
+}
+
+BOOL LLClickToTeleportTimer::tick()
+{
+	lldebugs << "Teleporting to " << mTeleportPos << llendl;
+	gAgent.teleportViaLocationLookAt(mTeleportPos);
+	return TRUE; // destroy the timer
+}
 
 LLToolPie::LLToolPie()
 :	LLTool(std::string("Pie")),
 	mMouseSteerX(-1),
 	mMouseSteerY(-1),
 	mBlockClickToWalk(false),
+	mBlockClickToTeleport(false),
+	mClickToTeleportTimer(NULL),
 	mClickAction(0),
 	mClickActionBuyEnabled( gSavedSettings.getBOOL("ClickActionBuyEnabled") ),
 	mClickActionPayEnabled( gSavedSettings.getBOOL("ClickActionPayEnabled") )
 	mClickAction = 0;
 }
 
+void LLToolPie::walkToClickedLocation()
+{
+	if(mAutoPilotDestination) { mAutoPilotDestination->markDead(); }
+	mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE);
+	mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal);
+	mAutoPilotDestination->setPixelSize(5);
+	mAutoPilotDestination->setColor(LLColor4U(170, 210, 190));
+	mAutoPilotDestination->setDuration(3.f);
+
+	handle_go_to();
+}
+
 // When we get object properties after left-clicking on an object
 // with left-click = buy, if it's the same object, do the buy.
 
 	mMouseButtonDown = false;
 
 	if (click_action == CLICK_ACTION_NONE				// not doing 1-click action
-		&& gSavedSettings.getBOOL("ClickToWalk")		// click to walk enabled
 		&& !gAgent.getFlying()							// don't auto-navigate while flying until that works
 		&& gAgentAvatarp
 		&& !gAgentAvatarp->isSitting()
-		&& !mBlockClickToWalk							// another behavior hasn't cancelled click to walk
 		&& !mPick.mPosGlobal.isExactlyZero()			// valid coordinates for pick
 		&& (mPick.mPickType == LLPickInfo::PICK_LAND	// we clicked on land
 			|| mPick.mObjectID.notNull()))				// or on an object
 	{
-		// handle special cases of steering picks
-		LLViewerObject* avatar_object = mPick.getObject();
+		if (gSavedSettings.getBOOL("ClickToWalk")
+			&& !mBlockClickToWalk)						// another behavior hasn't cancelled click to walk
+		{
+			// handle special cases of steering picks
+			LLViewerObject* avatar_object = mPick.getObject();
 
-		// get pointer to avatar
-		while (avatar_object && !avatar_object->isAvatar())
+			// get pointer to avatar
+			while (avatar_object && !avatar_object->isAvatar())
+			{
+				avatar_object = (LLViewerObject*)avatar_object->getParent();
+			}
+
+			if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf())
+			{
+				const F64 SELF_CLICK_WALK_DISTANCE = 3.0;
+				// pretend we picked some point a bit in front of avatar
+				mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE;
+			}
+			gAgentCamera.setFocusOnAvatar(TRUE, TRUE);
+			walkToClickedLocation();
+			LLFirstUse::notMoving(false);
+
+			return TRUE;
+		}
+		else if (gSavedSettings.getBOOL("ClickToTeleport") && !mBlockClickToTeleport)
 		{
-			avatar_object = (LLViewerObject*)avatar_object->getParent();
+			LLViewerObject* objp = mPick.getObject();
+			LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL;
+
+			bool is_in_world = mPick.mObjectID.notNull() && objp && !objp->isHUDAttachment();
+			bool is_land = mPick.mPickType == LLPickInfo::PICK_LAND;
+			bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch());
+			bool has_click_action = final_click_action(objp);
+
+			if (is_land || (is_in_world && !has_touch_handler && !has_click_action))
+			{
+				LLVector3d pos = mPick.mPosGlobal;
+				pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot();
+
+				if (gSavedSettings.getBOOL("DoubleClickAutoPilot"))
+				{
+					// defer for more than the double click interval.
+					scheduleTeleport(pos);
+				}
+				else
+				{
+					gAgent.teleportViaLocationLookAt(pos);
+				}
+				return TRUE;
+			}
 		}
-
-		if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf())
-		{
-			const F64 SELF_CLICK_WALK_DISTANCE = 3.0;
-			// pretend we picked some point a bit in front of avatar
-			mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE;
-		}
-		gAgentCamera.setFocusOnAvatar(TRUE, TRUE);
-		if(mAutoPilotDestination) { mAutoPilotDestination->markDead(); }
-		mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE);
-		mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal);
-		mAutoPilotDestination->setPixelSize(5);
-		mAutoPilotDestination->setColor(LLColor4U(170, 210, 190));
-		mAutoPilotDestination->setDuration(3.f);
-
-		handle_go_to();
-		LLFirstUse::notMoving(false);
-
-		mBlockClickToWalk = false;
-
-		return TRUE;
 	}
 	gViewerWindow->setCursor(UI_CURSOR_ARROW);
 	if (hasMouseCapture())
 	gAgentCamera.setLookAt(LOOKAT_TARGET_CONVERSATION, obj); // maybe look at object/person clicked on
 
 	mBlockClickToWalk = false;
+	mBlockClickToTeleport = false;
 	return LLTool::handleMouseUp(x, y, mask);
 }
 
 		llinfos << "LLToolPie handleDoubleClick (becoming mouseDown)" << llendl;
 	}
 
+	cancelScheduledTeleport();
+
 	if (gSavedSettings.getBOOL("DoubleClickAutoPilot"))
 	{
-		if (mPick.mPickType == LLPickInfo::PICK_LAND
-			&& !mPick.mPosGlobal.isExactlyZero())
+		// Avoid teleporting for the second time when user releases mouse button after double click.
+		mBlockClickToTeleport = true;
+
+		if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) ||
+			(mPick.mObjectID.notNull()  && !mPick.mPosGlobal.isExactlyZero()))
 		{
-			handle_go_to();
-			return TRUE;
-		}
-		else if (mPick.mObjectID.notNull()
-				 && !mPick.mPosGlobal.isExactlyZero())
-		{
-			handle_go_to();
+			walkToClickedLocation();
 			return TRUE;
 		}
 	}
 	return mMouseButtonDown && mMouseOutsideSlop && gSavedSettings.getBOOL("ClickToWalk");
 }
 
+void LLToolPie::scheduleTeleport(const LLVector3d& pos)
+{
+	// cancel previously scheduled teleport (if any)
+	cancelScheduledTeleport();
+
+	// and schedule new one
+	mClickToTeleportTimer = new LLClickToTeleportTimer(pos);
+}
+
+void LLToolPie::cancelScheduledTeleport()
+{
+	if (mClickToTeleportTimer)
+	{
+		delete mClickToTeleportTimer;
+	}
+}
+
 // true if x,y outside small box around start_x,start_y
 BOOL LLToolPie::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
 {

indra/newview/lltoolpie.h

 #include "llviewerwindow.h" // for LLPickInfo
 #include "llhudeffectblob.h" // for LLPointer<LLHudEffectBlob>, apparently
 
+class LLClickToTeleportTimer;
 class LLViewerObject;
 class LLObjectSelection;
 
 	LLViewerObject*		getClickActionObject() { return mClickActionObject; }
 	LLObjectSelection*	getLeftClickSelection() { return (LLObjectSelection*)mLeftClickSelection; }
 	void 				resetSelection();
+	void				walkToClickedLocation();
 	void				blockClickToWalk() { mBlockClickToWalk = true; }
 	void				stopClickToWalk();
 	
 	void startCameraSteering();
 	void stopCameraSteering();
 	bool inCameraSteerMode();
+	void scheduleTeleport(const LLVector3d& pos);
+	void cancelScheduledTeleport();
 
 private:
+	friend class LLClickToTeleportTimer;
+
 	bool				mMouseButtonDown;
 	bool				mMouseOutsideSlop;		// for this drag, has mouse moved outside slop region
 	S32					mMouseDownX;
 	LLPointer<LLHUDEffectBlob>	mMouseSteerGrabPoint;
 	bool				mClockwise;			
 	bool				mBlockClickToWalk;
+	bool				mBlockClickToTeleport;
+	LLClickToTeleportTimer*	mClickToTeleportTimer;
 	LLUUID				mMediaMouseCaptureID;
 	LLPickInfo			mPick;
 	LLPickInfo			mHoverPick;

indra/newview/skins/default/xui/en/panel_preferences_move.xml

 	 mouse_opaque="false"
 	 visible="true"
 	 width="18"
-   top_pad="2"
+   top_pad="10"
    left="30" />
+  <text
+   follows="left|top"
+   type="string"
+   length="1"
+   height="10"
+   layout="topleft"
+   left="78"
+   name="keyboard_lbl"
+   width="270"
+   top_delta="2">
+   Keyboard:
+  </text>
+  <check_box
+   control_name="ArrowKeysAlwaysMove"
+   follows="left|top"
+   height="20"
+   label="Arrow keys always move me"
+   layout="topleft"
+   left_delta="5"
+   name="arrow_keys_move_avatar_check"
+   width="237"
+   top_pad="5"/>
+  <check_box
+   control_name="AllowTapTapHoldRun"
+   follows="left|top"
+   height="20"
+   label="Tap-tap-hold to run"
+   layout="topleft"
+   left_delta="0"
+   name="tap_tap_hold_to_run"
+   width="237"
+   top_pad="0"/>
+  <text
+   follows="left|top"
+   type="string"
+   length="1"
+   height="10"
+   layout="topleft"
+   left="78"
+   name="mouse_lbl"
+   width="270"
+   top_pad="15">
+   Mouse:
+  </text>
   <check_box
    control_name="FirstPersonAvatarVisible"
    follows="left|top"
    height="20"
    label="Show me in Mouselook"
    layout="topleft"
-   left_pad="30"
+   left_delta="5"
    name="first_person_avatar_visible"
+   top_pad="5"
    width="256" />
   <text
    type="string"
    initial_value="2"
    layout="topleft"
    show_text="false"
-   left_pad="5"
+   left_pad="0"
    max_val="15"
    name="mouse_sensitivity"
    top_delta="-1"
    name="invert_mouse"
    top_delta="0"
    width="128" />
-  <check_box
-   control_name="ArrowKeysAlwaysMove"
+  <text
    follows="left|top"
-   height="20"
-   label="Arrow keys always move me"
+   type="string"
+   length="1"
+   height="10"
    layout="topleft"
-   left="78"
-   name="arrow_keys_move_avatar_check"
-   width="237"
-   top_pad="10"/>
-  <check_box
-   control_name="AllowTapTapHoldRun"
+   left="259"
+   name="single_click_lbl"
+   width="100"
+   top_pad="10">
+   Single-Click
+  </text>
+  <text
    follows="left|top"
-   height="20"
-   label="Tap-tap-hold to run"
+   type="string"
+   length="1"
+   height="10"
    layout="topleft"
-   left_delta="0"
-   name="tap_tap_hold_to_run"
-   width="237"
-   top_pad="0"/>
+   left="368"
+   name="double_click_lbl"
+   width="100"
+   top_delta="0">
+   Double-Click
+  </text>
   <check_box
    follows="left|top"
    height="20"
-   label="Double-Click to:"
+   label="Walk to Click Point:"
    layout="topleft"
-   left_delta="0"
-   name="double_click_chkbox"
+   left="83"
+   name="walk_to_chkbox"
    width="237"
-   top_pad="0">
+   top_pad="7">
    <check_box.commit_callback
-    function="Pref.CommitDoubleClickChekbox"/>
+    function="Pref.CommitClickToWalkCheckbox"/>
   </check_box>
   <radio_group
-     height="20"
-     layout="topleft"
-     left_delta="17"
-     top_pad="2"
-     name="double_click_action">
+   height="20"
+   layout="topleft"
+   left="280"
+   top_delta="3"
+   name="walk_trigger_radio"
+   width="200">
     <radio_item
      height="16"
-     label="Teleport"
+     label=""
      layout="topleft"
      left="0"
-     name="radio_teleport"
+     name="walk_single_click"
      top_delta="20"
      width="110" />
     <radio_item
      height="16"
-     label="Auto-pilot"
+     label=""
      left_pad="0"
      layout="topleft"
-     name="radio_autopilot"
+     name="walk_double_click"
      top_delta="0"
      width="75" />
     <radio_group.commit_callback
-	     function="Pref.CommitRadioDoubleClick"/>
+	     function="Pref.CommitWalkTriggerRadio"/>
+  </radio_group>
+  <check_box
+   follows="left|top"
+   height="20"
+   label="Teleport to Click Point:"
+   layout="topleft"
+   left="83"
+   name="teleport_to_chkbox"
+   width="237"
+   top_pad="0">
+   <check_box.commit_callback
+    function="Pref.CommitClickToTeleportCheckbox"/>
+  </check_box>
+  <radio_group
+   height="20"
+   layout="topleft"
+   left="280"
+   top_delta="3"
+   name="teleport_trigger_radio"
+   width="200">
+    <radio_item
+     height="16"
+     label=""
+     layout="topleft"
+     left="0"
+     name="teleport_single_click"
+     top_delta="20"
+     width="110" />
+    <radio_item
+     height="16"
+     label=""
+     left_pad="0"
+     layout="topleft"
+     name="teleport_double_click"
+     top_delta="0"
+     width="75" />
+    <radio_group.commit_callback
+	     function="Pref.CommitTeleportTriggerRadio"/>
   </radio_group>
   <button
    height="23"
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.