diff options
| -rw-r--r-- | indra/llmath/v3dmath.h | 9 | ||||
| -rw-r--r-- | indra/llui/llcheckboxctrl.cpp | 9 | ||||
| -rw-r--r-- | indra/llui/lllineeditor.cpp | 15 | ||||
| -rw-r--r-- | indra/llui/lllineeditor.h | 5 | ||||
| -rw-r--r-- | indra/llui/llmenugl.cpp | 627 | ||||
| -rw-r--r-- | indra/llui/llmenugl.h | 45 | ||||
| -rw-r--r-- | indra/llui/llview.cpp | 49 | ||||
| -rw-r--r-- | indra/llui/llview.h | 1 | ||||
| -rw-r--r-- | indra/llwindow/llkeyboard.cpp | 22 | ||||
| -rw-r--r-- | indra/llwindow/llkeyboard.h | 7 | ||||
| -rw-r--r-- | indra/llwindow/llkeyboardwin32.cpp | 37 | ||||
| -rw-r--r-- | indra/llwindow/llkeyboardwin32.h | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/keys.ini | 7 | ||||
| -rw-r--r-- | indra/newview/llchatbar.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/llfloatercolorpicker.cpp | 7 | ||||
| -rw-r--r-- | indra/newview/llimpanel.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/llinventorymodel.cpp | 12 | ||||
| -rw-r--r-- | indra/newview/llmanip.cpp | 4 | ||||
| -rw-r--r-- | indra/newview/llpreview.cpp | 10 | ||||
| -rw-r--r-- | indra/newview/lltexturectrl.cpp | 8 | ||||
| -rw-r--r-- | indra/newview/llviewermenu.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewerwindow.cpp | 28 | 
22 files changed, 640 insertions, 268 deletions
| diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h index d8feb10757..5d414df5d6 100644 --- a/indra/llmath/v3dmath.h +++ b/indra/llmath/v3dmath.h @@ -405,5 +405,14 @@ inline BOOL are_parallel(const LLVector3d &a, const LLVector3d &b, const F64 eps  		return TRUE;  	}  	return FALSE; +  } + +inline LLVector3d projected_vec(const LLVector3d &a, const LLVector3d &b) +{ +	LLVector3d project_axis = b; +	project_axis.normVec(); +	return project_axis * (a * project_axis); +} +  #endif // LL_V3DMATH_H diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 3b054d2fec..fde27132e6 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -274,8 +274,6 @@ LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto  	LLString label("");  	node->getAttributeString("label", label); -	BOOL initial_value = FALSE; -	  	LLFontGL* font = LLView::selectFont(node);  	BOOL radio_style = FALSE; @@ -297,9 +295,12 @@ LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto  		font,  		callback,  		NULL, -		initial_value, +		FALSE,  		radio_style); // if true, draw radio button style icons +	BOOL initial_value = checkbox->getValue().asBoolean(); +	node->getAttributeBOOL("initial_value", initial_value); +  	LLColor4 color;  	color = LLUI::sColorsGroup->getColor( "LabelTextColor" );  	LLUICtrlFactory::getAttributeColor(node,"text_enabled_color", color); @@ -309,6 +310,8 @@ LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto  	LLUICtrlFactory::getAttributeColor(node,"text_disabled_color", color);  	checkbox->setDisabledColor(color); +	checkbox->setValue(initial_value); +  	checkbox->initFromXML(node, parent);  	return checkbox; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 41049fdf1f..46d66b3cd4 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -72,6 +72,7 @@ public:  		ed->mSelectionStart = mSelectionStart;  		ed->mSelectionEnd = mSelectionEnd;  		ed->mText = mText; +		ed->mPrevText = mText;  	}  	LLString getText()   { return mText; } @@ -110,6 +111,7 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect,  		mBorderLeft(0),  		mBorderRight(0),  		mCommitOnFocusLost( TRUE ), +		mRevertOnEsc( TRUE ),  		mKeystrokeCallback( keystroke_callback ),  		mFocusLostCallback( focus_lost_callback ),  		mIsSelecting( FALSE ), @@ -151,7 +153,7 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect,  	mScrollTimer.reset();  	setText(default_text); - +	  	setCursor(mText.length());  	// Scalable UI somehow made these rectangles off-by-one. @@ -195,7 +197,7 @@ void LLLineEditor::onFocusLost()  		mFocusLostCallback( this, mCallbackUserData );  	} -	if( mCommitOnFocusLost )  +	if( mCommitOnFocusLost && mText.getString() != mPrevText)   	{  		onCommit();  	} @@ -281,6 +283,7 @@ void LLLineEditor::setText(const LLString &new_text)  		deselect();  	}  	setCursor(llmin((S32)mText.length(), getCursor())); +	mPrevText = mText;  } @@ -1064,6 +1067,14 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  		}  		break; +	case KEY_ESCAPE: +	    if (mRevertOnEsc && mText.getString() != mPrevText) +		{ +			setText(mPrevText); +			// Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged) +		} +		break; +		  	default:  		break;  	} diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 1df5dd88f7..ef2f43a1d3 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -127,9 +127,10 @@ public:  	void			setSelection(S32 start, S32 end);  	void			setCommitOnFocusLost( BOOL b )	{ mCommitOnFocusLost = b; } +	void			setRevertOnEsc( BOOL b )		{ mRevertOnEsc = b; }  	void setCursorColor(const LLColor4& c)			{ mCursorColor = c; } -	const LLColor4& getCursorColor() const		{ return mCursorColor; } +	const LLColor4& getCursorColor() const			{ return mCursorColor; }  	void setFgColor( const LLColor4& c )			{ mFgColor = c; }  	void setReadOnlyFgColor( const LLColor4& c )	{ mReadOnlyFgColor = c; } @@ -202,6 +203,7 @@ protected:  protected:  	LLUIString		mText;					// The string being edited. +	LLString		mPrevText;				// Saved string for 'ESC' revert  	LLUIString		mLabel;					// text label that is visible when no user text provided  	LLViewBorder* mBorder; @@ -217,6 +219,7 @@ protected:  	S32			mBorderRight;  	BOOL		mCommitOnFocusLost; +	BOOL		mRevertOnEsc;  	void		(*mKeystrokeCallback)( LLLineEditor* caller, void* userdata );  	void		(*mFocusLostCallback)( LLLineEditor* caller, void* userdata ); diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index f8fcefd11d..650596c7f7 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -90,7 +90,9 @@ LLColor4 LLMenuItemGL::sDisabledColor( 0.5f, 0.5f, 0.5f, 1.0f );  LLColor4 LLMenuItemGL::sHighlightBackground( 0.0f, 0.0f, 0.7f, 1.0f );  LLColor4 LLMenuItemGL::sHighlightForeground( 1.0f, 1.0f, 1.0f, 1.0f );  BOOL LLMenuItemGL::sDropShadowText = TRUE; +  LLColor4 LLMenuGL::sDefaultBackgroundColor( 0.25f, 0.25f, 0.25f, 0.75f ); +BOOL LLMenuGL::sKeyboardMode = FALSE;  LLViewHandle LLMenuHolderGL::sItemLastSelectedHandle;  LLFrameTimer LLMenuHolderGL::sItemActivationTimer; @@ -379,41 +381,65 @@ void LLMenuItemGL::buildDrawLabel( void )  	mDrawAccelLabel = st;  } +void LLMenuItemGL::doIt( void ) +{ +	// close all open menus by default +	// if parent menu is actually visible (and we are not triggering menu item via accelerator) +	if (!getMenu()->getTornOff() && getMenu()->getVisible()) +	{ +		((LLMenuHolderGL*)getMenu()->getParent())->hideMenus(); +	} +} +  // set the hover status (called by it's menu)   void LLMenuItemGL::setHighlight( BOOL highlight )  { +	if (highlight) +	{ +		getMenu()->clearHoverItem(); +	}  	mHighlight = highlight;  } -// determine if this object is active +// determine if this object represents an active sub-menu  BOOL LLMenuItemGL::isActive( void ) const  {  	return FALSE;  } +// determine if this object represents an open sub-menu +BOOL LLMenuItemGL::isOpen( void ) const +{ +	return FALSE; +} +  BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )  { -	if (mHighlight &&  -		getMenu()->getVisible() && -		(!getMenu()->getTornOff() || ((LLFloater*)getMenu()->getParent())->hasFocus())) +	if (getHighlight() &&  +		getMenu()->isOpen())  	{  		if (key == KEY_UP)  		{ +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); +  			getMenu()->highlightPrevItem(this);  			return TRUE;  		}  		else if (key == KEY_DOWN)  		{ +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); +  			getMenu()->highlightNextItem(this);  			return TRUE;  		}  		else if (key == KEY_RETURN && mask == MASK_NONE)  		{ +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); +  			doIt(); -			if (!getMenu()->getTornOff()) -			{ -				((LLMenuHolderGL*)getMenu()->getParent())->hideMenus(); -			}  			return TRUE;  		}  	} @@ -427,8 +453,11 @@ BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK )  	//	<< llendl;  	if (mEnabled)  	{ +		// switch to mouse navigation mode +		LLMenuGL::setKeyboardMode(FALSE); +  		doIt(); -		mHighlight = FALSE; +		setHighlight(FALSE);  		make_ui_sound("UISndClickRelease");  		return TRUE;  	} @@ -444,7 +473,8 @@ void LLMenuItemGL::draw( void )  	// that until the functionality is finalized.  	// HACK: Brief items don't highlight.  Pie menu takes care of it.  JC -	if( mHighlight && !mBriefItem) +	// let disabled items be highlighted, just don't draw them as such +	if( getEnabled() && getHighlight() && !mBriefItem)  	{  		glColor4fv( sHighlightBackground.mV );  		gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 ); @@ -458,7 +488,7 @@ void LLMenuItemGL::draw( void )  		font_style |= LLFontGL::DROP_SHADOW;  	} -	if ( mHighlight ) +	if ( getEnabled() && getHighlight() )  	{  		color = sHighlightForeground;  	} @@ -498,11 +528,8 @@ void LLMenuItemGL::draw( void )  		}  	} -	// underline navigation key -	BOOL draw_jump_key = gKeyboard->currentMask(FALSE) == MASK_ALT &&  -								(!getMenu()->getHighlightedItem() || !getMenu()->getHighlightedItem()->isActive()) && -								(!getMenu()->getTornOff()); -	if (draw_jump_key) +	// underline "jump" key +	if (getMenu()->jumpKeysActive())  	{  		LLString upper_case_label = mLabel.getString();  		LLString::toUpper(upper_case_label); @@ -666,8 +693,6 @@ void LLMenuItemTearOffGL::doIt()  			getMenu()->highlightNextItem(this);  		} -		// grab menu holder before this menu is parented to a floater -		LLMenuHolderGL* menu_holder = ((LLMenuHolderGL*)getMenu()->getParent());  		getMenu()->arrange();  		LLFloater* parent_floater = LLFloater::getFloaterByHandle(mParentHandle); @@ -677,22 +702,17 @@ void LLMenuItemTearOffGL::doIt()  			parent_floater->addDependentFloater(tear_off_menu, FALSE);  		} -		// hide menus -		// only do it if the menu is open, not being triggered via accelerator -		if (getMenu()->getVisible()) -		{ -			menu_holder->hideMenus(); -		} -  		// give focus to torn off menu because it will have been taken away  		// when parent menu closes  		tear_off_menu->setFocus(TRUE);  	} +	LLMenuItemGL::doIt();  }  void LLMenuItemTearOffGL::draw()  { -	if( mHighlight && !mBriefItem) +	// disabled items can be highlighted, but shouldn't render as such +	if( getEnabled() && getHighlight() && !mBriefItem)  	{  		glColor4fv( sHighlightBackground.mV );  		gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 ); @@ -910,6 +930,7 @@ void LLMenuItemCallGL::doIt( void )  	}  	LLPointer<LLEvent> fired_event = new LLEvent(this);  	fireEvent(fired_event, "on_click"); +	LLMenuItemGL::doIt();  }  EWidgetType LLMenuItemCallGL::getWidgetType() const  @@ -1118,6 +1139,7 @@ void LLMenuItemToggleGL::doIt( void )  	//llinfos << "LLMenuItemToggleGL::doIt " << mLabel.c_str() << llendl;  	*mToggle = !(*mToggle);  	buildDrawLabel(); +	LLMenuItemGL::doIt();  } @@ -1159,6 +1181,7 @@ public:  	virtual void doIt( void );  	virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); +	virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);  	// set the hover status (called by it's menu) and if the object is  	// active. This is used for behavior transfer. @@ -1166,7 +1189,9 @@ public:  	virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); -	virtual BOOL isActive() const	{ return !mBranch->getTornOff() && mBranch->getVisible(); } +	virtual BOOL isActive() const; + +	virtual BOOL isOpen() const;  	LLMenuGL *getBranch() const { return mBranch; } @@ -1178,6 +1203,8 @@ public:  	virtual void draw();  	virtual void setEnabledSubMenus(BOOL enabled); + +	virtual void openMenu();  };  LLMenuItemBranchGL::LLMenuItemBranchGL( const LLString& name, const LLString& label, LLMenuGL* branch, @@ -1215,6 +1242,9 @@ BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)  {  	if (mEnabled)  	{ +		// switch to mouse navigation mode +		LLMenuGL::setKeyboardMode(FALSE); +  		doIt();  		make_ui_sound("UISndClickRelease");  	} @@ -1269,50 +1299,14 @@ void LLMenuItemBranchGL::buildDrawLabel( void )  // doIt() - do the primary functionality of the menu item.  void LLMenuItemBranchGL::doIt( void )  { -	if (mBranch->getTornOff()) +	openMenu(); + +	// keyboard navigation automatically propagates highlight to sub-menu +	// to facilitate fast menu control via jump keys +	if (LLMenuGL::getKeyboardMode() && !mBranch->getHighlightedItem())  	{ -		gFloaterView->bringToFront((LLFloater*)mBranch->getParent()); -		// this might not be necessary, as torn off branches don't get focus and hence no highligth  		mBranch->highlightNextItem(NULL);  	} -	else if( !mBranch->getVisible() ) -	{ -		mBranch->arrange(); - -		LLRect rect = mBranch->getRect(); -		// calculate root-view relative position for branch menu -		S32 left = mRect.mRight; -		S32 top = mRect.mTop - mRect.mBottom; - -		localPointToOtherView(left, top, &left, &top, mBranch->getParent()); - -		rect.setLeftTopAndSize( left, top, -								rect.getWidth(), rect.getHeight() ); - -		if (mBranch->getCanTearOff()) -		{ -			rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); -		} -		mBranch->setRect( rect ); -		S32 x = 0; -		S32 y = 0; -		mBranch->localPointToOtherView( 0, 0, &x, &y, mBranch->getParent() );  -		S32 delta_x = 0; -		S32 delta_y = 0; -		if( y < 0 ) -		{ -			delta_y = -y; -		} - -		S32 window_width = mBranch->getParent()->getRect().getWidth(); -		if( x > window_width - rect.getWidth() ) -		{ -			// move sub-menu over to left side -			delta_x = llmax(-x, (-1 * (rect.getWidth() + mRect.getWidth()))); -		} -		mBranch->translate( delta_x, delta_y ); -		mBranch->setVisible( TRUE ); -	}  }  BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) @@ -1331,9 +1325,34 @@ BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)  	return handled;  } +BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +{ +	BOOL handled = FALSE; +	if (called_from_parent) +	{ +		handled = mBranch->handleUnicodeChar(uni_char, TRUE); +	} + +	if (!handled) +	{ +		handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent); +	} + +	return handled; +} + +  // set the hover status (called by it's menu)  void LLMenuItemBranchGL::setHighlight( BOOL highlight )  { +	if (highlight == getHighlight()) return; + +	// make sure only yourself is highlighted +	if (highlight) +	{ +		getMenu()->clearHoverItem(); +	} +  	BOOL auto_open = mEnabled && (!mBranch->getVisible() || mBranch->getTornOff());  	// torn off menus don't open sub menus on hover unless they have focus  	if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus()) @@ -1351,7 +1370,7 @@ void LLMenuItemBranchGL::setHighlight( BOOL highlight )  	{  		if(auto_open)  		{ -			doIt(); +			openMenu();  		}  	}  	else @@ -1378,10 +1397,22 @@ void LLMenuItemBranchGL::draw()  	LLMenuItemGL::draw();  	if (mBranch->getVisible() && !mBranch->getTornOff())  	{ -		mHighlight = TRUE; +		setHighlight(TRUE);  	}  } +// determine if this object is active +// which, for branching menus, means the branch is open and has "focus" +BOOL LLMenuItemBranchGL::isActive( void ) const +{ +	return isOpen() && mBranch->getHighlightedItem(); +} + +BOOL LLMenuItemBranchGL::isOpen( void ) const +{ +	return mBranch->isOpen(); +} +  void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)  {  	if (mBranch->getParent() == NULL) @@ -1403,7 +1434,14 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_par  {  	if (getMenu()->getVisible() && mBranch->getVisible() && key == KEY_LEFT)  	{ +		// switch to keyboard navigation mode +		LLMenuGL::setKeyboardMode(TRUE); +  		BOOL handled = mBranch->clearHoverItem(); +		if (mBranch->getTornOff()) +		{ +			((LLFloater*)mBranch->getParent())->setFocus(FALSE); +		}  		if (handled && getMenu()->getTornOff())  		{  			((LLFloater*)getMenu()->getParent())->setFocus(TRUE); @@ -1411,12 +1449,14 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_par  		return handled;  	} -	if (mHighlight &&  -		getMenu()->getVisible() && -		// ignore keystrokes on background torn-off menus -		(!getMenu()->getTornOff() || ((LLFloater*)getMenu()->getParent())->hasFocus()) && +	if (getEnabled() && +		getHighlight() &&  +		getMenu()->isOpen() &&   		key == KEY_RIGHT && !mBranch->getHighlightedItem())  	{ +		// switch to keyboard navigation mode +		LLMenuGL::setKeyboardMode(TRUE); +  		LLMenuItemGL* itemp = mBranch->highlightNextItem(NULL);  		if (itemp)  		{ @@ -1427,6 +1467,54 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_par  	return LLMenuItemGL::handleKeyHere(key, mask, called_from_parent);  } +void LLMenuItemBranchGL::openMenu() +{ +	if (mBranch->getTornOff()) +	{ +		gFloaterView->bringToFront((LLFloater*)mBranch->getParent()); +		// this might not be necessary, as torn off branches don't get focus and hence no highligth +		mBranch->highlightNextItem(NULL); +	} +	else if( !mBranch->getVisible() ) +	{ +		mBranch->arrange(); + +		LLRect rect = mBranch->getRect(); +		// calculate root-view relative position for branch menu +		S32 left = mRect.mRight; +		S32 top = mRect.mTop - mRect.mBottom; + +		localPointToOtherView(left, top, &left, &top, mBranch->getParent()); + +		rect.setLeftTopAndSize( left, top, +								rect.getWidth(), rect.getHeight() ); + +		if (mBranch->getCanTearOff()) +		{ +			rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); +		} +		mBranch->setRect( rect ); +		S32 x = 0; +		S32 y = 0; +		mBranch->localPointToOtherView( 0, 0, &x, &y, mBranch->getParent() );  +		S32 delta_x = 0; +		S32 delta_y = 0; +		if( y < 0 ) +		{ +			delta_y = -y; +		} + +		S32 window_width = mBranch->getParent()->getRect().getWidth(); +		if( x > window_width - rect.getWidth() ) +		{ +			// move sub-menu over to left side +			delta_x = llmax(-x, (-1 * (rect.getWidth() + mRect.getWidth()))); +		} +		mBranch->translate( delta_x, delta_y ); +		mBranch->setVisible( TRUE ); +	} +} +  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLMenuItemBranchDownGL @@ -1456,14 +1544,13 @@ public:  	// called to rebuild the draw label  	virtual void buildDrawLabel( void ); -	// doIt() - do the primary funcationality of the menu item. -	virtual void doIt( void ); +	// handles opening, positioning, and arranging the menu branch associated with this item +	virtual void openMenu( void );  	// set the hover status (called by it's menu) and if the object is  	// active. This is used for behavior transfer.  	virtual void setHighlight( BOOL highlight ); -	// determine if this object is active  	virtual BOOL isActive( void ) const;  	// LLView functionality @@ -1502,8 +1589,7 @@ void LLMenuItemBranchDownGL::buildDrawLabel( void )  	mDrawAccelLabel = st;  } -// doIt() - do the primary funcationality of the menu item. -void LLMenuItemBranchDownGL::doIt( void ) +void LLMenuItemBranchDownGL::openMenu( void )  {  	if( mBranch->getVisible() && !mBranch->getTornOff() )  	{ @@ -1544,21 +1630,23 @@ void LLMenuItemBranchDownGL::doIt( void )  			}  			mBranch->translate( delta_x, 0 ); -			// *TODO: get menuholder lookup working more generically -			// hide existing menus -			if (!mBranch->getTornOff()) -			{ -				((LLMenuHolderGL*)mBranch->getParent())->hideMenus(); -			} - +			setHighlight(TRUE);  			mBranch->setVisible( TRUE );  		} + +  	}  }  // set the hover status (called by it's menu)  void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )  { +	if (highlight == getHighlight()) return; + +	if (highlight) +	{ +		getMenu()->clearHoverItem(); +	}  	mHighlight = highlight;  	if( !highlight)  	{ @@ -1574,22 +1662,18 @@ void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )  	}  } -// determine if this object is active -// which, for branching menus, means the branch is open and has "focus" -BOOL LLMenuItemBranchDownGL::isActive( void ) const +BOOL LLMenuItemBranchDownGL::isActive() const  { -	if (mBranch->getTornOff()) -	{ -		return ((LLFloater*)mBranch->getParent())->hasFocus(); -	} -	else -	{ -		return mBranch->getVisible(); -	} +	// for top level menus, being open is sufficient to be considered  +	// active, because clicking on them with the mouse will open +	// them, without moving keyboard focus to them +	return isOpen();  }  BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )  { +	// switch to mouse control mode +	LLMenuGL::setKeyboardMode(FALSE);  	doIt();  	make_ui_sound("UISndClick");  	return TRUE; @@ -1611,12 +1695,17 @@ BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)  BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)  { -	if (mHighlight && getMenu()->getVisible() && mBranch->getVisible()) +	BOOL menu_open = mBranch->getVisible(); +	if (getHighlight() && getMenu()->getVisible())  	{  		if (key == KEY_LEFT)  		{ +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); +  			LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this); -			if (itemp) +			// open new menu only if previous menu was open +			if (itemp && itemp->getEnabled() && menu_open)  			{  				itemp->doIt();  			} @@ -1625,8 +1714,12 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_  		}  		else if (key == KEY_RIGHT)  		{ +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); +  			LLMenuItemGL* itemp = getMenu()->highlightNextItem(this); -			if (itemp) +			// open new menu only if previous menu was open +			if (itemp && itemp->getEnabled() && menu_open)  			{  				itemp->doIt();  			} @@ -1635,18 +1728,24 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_  		}  		else if (key == KEY_DOWN)  		{ -			if (!mBranch->getTornOff()) +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); + +			if (getEnabled() && !isActive())  			{ -				mBranch->setVisible(TRUE); +				doIt();  			}  			mBranch->highlightNextItem(NULL);  			return TRUE;  		}  		else if (key == KEY_UP)  		{ -			if (!mBranch->getTornOff()) +			// switch to keyboard navigation mode +			LLMenuGL::setKeyboardMode(TRUE); + +			if (getEnabled() && !isActive())  			{ -				mBranch->setVisible(TRUE); +				doIt();  			}  			mBranch->highlightPrevItem(NULL);  			return TRUE; @@ -1658,7 +1757,13 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_  void LLMenuItemBranchDownGL::draw( void )  { -	if( mHighlight ) +	//FIXME: try removing this +	if (mBranch->getVisible() && !mBranch->getTornOff()) +	{ +		setHighlight(TRUE); +	} + +	if( getHighlight() )  	{  		glColor4fv( sHighlightBackground.mV );  		gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 ); @@ -1671,7 +1776,7 @@ void LLMenuItemBranchDownGL::draw( void )  	}  	LLColor4 color; -	if (mHighlight) +	if (getHighlight())  	{  		color = sHighlightForeground;  	} @@ -1685,18 +1790,10 @@ void LLMenuItemBranchDownGL::draw( void )  	}  	mFont->render( mLabel.getWString(), 0, (F32)mRect.getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,  				   LLFontGL::HCENTER, LLFontGL::BOTTOM, font_style ); -	// if branching menu is closed clear out highlight -	if (mHighlight && ((!mBranch->getVisible() /*|| mBranch->getTornOff()*/) && !mGotHover)) -	{ -		setHighlight(FALSE); -	} -	// underline navigation key -	BOOL draw_jump_key = gKeyboard->currentMask(FALSE) == MASK_ALT &&  -								(!getMenu()->getHighlightedItem() || !getMenu()->getHighlightedItem()->isActive()) && -								(!getMenu()->getTornOff()); // torn off menus don't use jump keys, too complicated -	if (draw_jump_key) +	// underline navigation key +	if (getMenu()->jumpKeysActive())  	{  		LLString upper_case_label = mLabel.getString();  		LLString::toUpper(upper_case_label); @@ -2078,7 +2175,8 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory  				}  				item = new_item;  				item->setLabel(item_label); -				item->setJumpKey(jump_key); +				if (jump_key != KEY_NONE) +					item->setJumpKey(jump_key);  			}  			if (item != NULL) @@ -2089,6 +2187,50 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory  	}  } +// are we the childmost active menu and hence our jump keys should be enabled? +// or are we a free-standing torn-off menu (which uses jump keys too) +BOOL LLMenuGL::jumpKeysActive() +{ +	LLMenuItemGL* highlighted_item = getHighlightedItem(); +	BOOL active = getVisible() && getEnabled(); +	if (getTornOff()) +	{ +		// activation of jump keys on torn off menus controlled by keyboard focus +		active = active && ((LLFloater*)getParent())->hasFocus(); + +	} +	else +	{ +		// Are we the terminal active menu? +		// Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus) +		// and we don't have a highlighted menu item pointing to an active sub-menu +		active = active && (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active... +		                && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active +	} +	return active; +} + +BOOL LLMenuGL::isOpen() +{ +	if (getTornOff()) +	{ +		LLMenuItemGL* itemp = getHighlightedItem(); +		// if we have an open sub-menu, then we are considered part of  +		// the open menu chain even if we don't have focus +		if (itemp && itemp->isOpen()) +		{ +			return TRUE; +		} +		// otherwise we are only active if we have keyboard focus +		return ((LLFloater*)getParent())->hasFocus(); +	} +	else +	{ +		// normally, menus are hidden as soon as the user focuses +		// on another menu, so just use the visibility criterion +		return getVisible(); +	} +}  // static  LLView* LLMenuGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)  { @@ -2456,21 +2598,27 @@ void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)  	arrange();  } -void LLMenuGL::handleJumpKey(KEY key) +BOOL LLMenuGL::handleJumpKey(KEY key)  { +	// must perform case-insensitive comparison, so just switch to uppercase input key +	key = toupper(key);  	navigation_key_map_t::iterator found_it = mJumpKeys.find(key);  	if(found_it != mJumpKeys.end() && found_it->second->getEnabled())  	{ -		clearHoverItem(); +		// switch to keyboard navigation mode +		LLMenuGL::setKeyboardMode(TRUE); + +		// force highlight to close old menus and any open sub-menus + +		//clearHoverItem();  		// force highlight to close old menus and open and sub-menus  		found_it->second->setHighlight(TRUE);  		found_it->second->doIt(); -		if (!found_it->second->isActive() && !getTornOff()) -		{ -			// parent is a menu holder, because this is not a menu bar -			((LLMenuHolderGL*)getParent())->hideMenus(); -		} +  	} +	// if we are navigating the menus, we need to eat the keystroke +	// so rest of UI doesn't handle it +	return TRUE;  } @@ -2723,10 +2871,6 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa  		// skip separators and disabled items  		if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getName() != SEPARATOR_NAME)  		{ -			if (cur_item) -			{ -				cur_item->setHighlight(FALSE); -			}  			(*prev_item_iter)->setHighlight(TRUE);  			return (*prev_item_iter);  		} @@ -2821,17 +2965,11 @@ BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)  	return FALSE;  } -BOOL LLMenuGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) +BOOL LLMenuGL::handleUnicodeCharHere( llwchar uni_char, BOOL called_from_parent )  { -	if (key < KEY_SPECIAL && getVisible() && getEnabled() && mask == MASK_ALT) +	if (jumpKeysActive())  	{ -		if (getTornOff()) -		{ -			// torn off menus do not handle jump keys (for now, the interaction is complex) -			return FALSE; -		} -		handleJumpKey(key); -		return TRUE; +		return handleJumpKey((KEY)uni_char);  	}  	return FALSE;  } @@ -2839,8 +2977,9 @@ BOOL LLMenuGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )  BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )  {  	// leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU -	S32 mouse_delta_x = x - mLastMouseX; -	S32 mouse_delta_y = y - mLastMouseY; +	BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; +	S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; +	S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;  	LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);  	mouse_dir.normVec();  	LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY); @@ -2852,8 +2991,7 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )  	mLastMouseY = y;  	// don't change menu focus unless mouse is moving or alt key is not held down -	if ((gKeyboard->currentMask(FALSE) != MASK_ALT ||  -			llabs(mMouseVelX) > 0 ||  +	if ((llabs(mMouseVelX) > 0 ||   			llabs(mMouseVelY) > 0) &&  		(!mHasSelection ||  		//(mouse_delta_x == 0 && mouse_delta_y == 0) || @@ -2883,7 +3021,9 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )  			//RN: always call handleHover to track mGotHover status  			// but only set highlight when mouse is moving  			if( viewp->getVisible() &&  -				viewp->getEnabled() && +				//RN: allow disabled items to be highlighted to preserve "active" menus when +				// moving mouse through them +				//viewp->getEnabled() &&   				viewp->pointInView(local_x, local_y) &&   				viewp->handleHover(local_x, local_y, mask))  			{ @@ -2891,6 +3031,7 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )  				if (mouse_delta_x != 0 || mouse_delta_y != 0)  				{  					((LLMenuItemGL*)viewp)->setHighlight(TRUE); +					LLMenuGL::setKeyboardMode(FALSE);  				}  				mHasSelection = TRUE;  			} @@ -2900,19 +3041,6 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )  	return TRUE;  } -BOOL LLMenuGL::handleMouseUp( S32 x, S32 y, MASK mask ) -{ -	if( LLView::childrenHandleMouseUp( x, y, mask ) ) -	{ -		if (!getTornOff()) -		{ -			((LLMenuHolderGL*)getParent())->hideMenus(); -		} -	} - -	return TRUE; -} -  void LLMenuGL::draw( void )  {  	if (mDropShadowed && !mTornOff) @@ -2946,6 +3074,10 @@ void LLMenuGL::setVisible(BOOL visible)  		{  			mFadeTimer.start();  			clearHoverItem(); +			// reset last known mouse coordinates so +			// we don't spoof a mouse move next time we're opened +			mLastMouseX = 0; +			mLastMouseY = 0;  		}  		else  		{ @@ -2977,12 +3109,12 @@ LLMenuGL* LLMenuGL::getChildMenuByName(const LLString& name, BOOL recurse) const  	return NULL;  } -BOOL LLMenuGL::clearHoverItem(BOOL include_active) +BOOL LLMenuGL::clearHoverItem()  {  	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)  	{  		LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it; -		if (itemp->getHighlight() && (include_active || !itemp->isActive())) +		if (itemp->getHighlight())  		{  			itemp->setHighlight(FALSE);  			return TRUE; @@ -3243,10 +3375,8 @@ BOOL LLPieMenu::handleHover( S32 x, S32 y, MASK mask )  		if (item != mHoverItem)  		{ -			BOOL active = FALSE;  			if (mHoverItem)  			{ -				active = mHoverItem->isActive();  				mHoverItem->setHighlight( FALSE );  			}  			mHoverItem = item; @@ -3824,6 +3954,7 @@ LLMenuBarGL::LLMenuBarGL( const LLString& name ) : LLMenuGL ( name, name )  	mHorizontalLayout = TRUE;  	setCanTearOff(FALSE);  	mKeepFixedSize = TRUE; +	mAltKeyTrigger = FALSE;  }  // Default destructor @@ -3934,15 +4065,112 @@ LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory  	return menubar;  } -void LLMenuBarGL::handleJumpKey(KEY key) +BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)  { +	if (getHighlightedItem() && mask == MASK_NONE) +	{ +		// unmodified key accelerators are ignored when navigating menu +		// (but are used as jump keys so will still work when appropriate menu is up) +		return FALSE; +	} +	BOOL result = LLMenuGL::handleAcceleratorKey(key, mask); +	if (result && mask & MASK_ALT) +	{ +		// ALT key used to trigger hotkey, don't use as shortcut to open menu +		mAltKeyTrigger = FALSE; +	} + +	if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key)) +	{ +		if (getHighlightedItem()) +		{ +			clearHoverItem(); +		} +		else +		{ +			highlightNextItem(NULL); +			LLMenuGL::setKeyboardMode(TRUE); +		} +		return TRUE; +	} + +	return result; +} + +BOOL LLMenuBarGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) +{ +	if(key == KEY_ALT) +	{ +		mAltKeyTrigger = TRUE; +	} +	// before processing any other key, check to see if ALT key has triggered menu access +	checkMenuTrigger(); + +	return LLMenuGL::handleKeyHere(key, mask, called_from_parent); +} + +BOOL LLMenuBarGL::handleJumpKey(KEY key) +{ +	// perform case-insensitive comparison +	key = toupper(key);  	navigation_key_map_t::iterator found_it = mJumpKeys.find(key);  	if(found_it != mJumpKeys.end() && found_it->second->getEnabled())  	{ -		clearHoverItem(); +		// switch to keyboard navigation mode +		LLMenuGL::setKeyboardMode(TRUE); +  		found_it->second->setHighlight(TRUE);  		found_it->second->doIt();  	} +	return TRUE; +} + +void LLMenuBarGL::draw() +{ +	LLMenuItemGL* itemp = getHighlightedItem(); +	// If we are in mouse-control mode and the mouse cursor is not hovering over +	// the current highlighted menu item and it isn't open, then remove the highlight. +	// This is done via a polling mechanism here, as we don't receive notifications when +	// the mouse cursor moves off of us +	if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode()) +	{ +		clearHoverItem(); +	} + +	checkMenuTrigger(); + +	LLMenuGL::draw(); +} + +void LLMenuBarGL::checkMenuTrigger() +{ +	// has the ALT key been pressed and subsequently released? +	if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT)) +	{ +		// if alt key was released quickly, treat it as a menu access key +		// otherwise it was probably an Alt-zoom or similar action +		if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= LLUI::sConfigGroup->getF32("MenuAccessKeyTime") || +			gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2) +		{ +			if (getHighlightedItem()) +			{ +				clearHoverItem(); +			} +			else +			{ +				highlightNextItem(NULL); +				LLMenuGL::setKeyboardMode(TRUE); +			} +		} +		mAltKeyTrigger = FALSE; +	} +} + +BOOL LLMenuBarGL::jumpKeysActive() +{ +	// require item to be highlighted to activate key triggers +	// as menu bars are always visible +	return getHighlightedItem() && LLMenuGL::jumpKeysActive();  }  // rearrange the child rects so they fit the shape of the menu bar. @@ -4013,8 +4241,9 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )  	BOOL handled = FALSE;  	LLView* active_menu = NULL; -	S32 mouse_delta_x = x - mLastMouseX; -	S32 mouse_delta_y = y - mLastMouseY; +	BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0; +	S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX; +	S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;  	mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);  	mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);  	mLastMouseX = x; @@ -4022,13 +4251,13 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )  	// if nothing currently selected or mouse has moved since last call, pick menu item via mouse  	// otherwise let keyboard control it -	if (!getHighlightedItem() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0) +	if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)  	{  		// find current active menu  		for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)  		{  			LLView* viewp = *child_it; -			if (((LLMenuItemGL*)viewp)->isActive()) +			if (((LLMenuItemGL*)viewp)->isOpen())  			{  				active_menu = viewp;  			} @@ -4050,6 +4279,7 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )  				if (active_menu && active_menu != viewp)  				{  					((LLMenuItemGL*)viewp)->doIt(); +					LLMenuGL::setKeyboardMode(FALSE);  				}  			}  		} @@ -4249,23 +4479,6 @@ LLTearOffMenu::~LLTearOffMenu()  void LLTearOffMenu::draw()  { -	if (hasFocus()) -	{ -		LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem(); -		while(parent_menu_item) -		{ -			if (parent_menu_item->getMenu()->getVisible()) -			{ -				parent_menu_item->setHighlight(TRUE); -				parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); -			} -			else -			{ -				break; -			} -		} -	} -  	mMenu->setBackgroundVisible(mBgOpaque);  	mMenu->arrange(); @@ -4290,6 +4503,21 @@ void LLTearOffMenu::onFocusReceived()  	{  		mMenu->highlightNextItem(NULL);  	} + +	// parent menu items get highlights so navigation logic keeps working +	LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem(); +	while(parent_menu_item) +	{ +		if (parent_menu_item->getMenu()->getVisible()) +		{ +			parent_menu_item->setHighlight(TRUE); +			parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem(); +		} +		else +		{ +			break; +		} +	}  }  void LLTearOffMenu::onFocusLost() @@ -4298,6 +4526,41 @@ void LLTearOffMenu::onFocusLost()  	mMenu->clearHoverItem();  } +BOOL LLTearOffMenu::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +{ +	// pass keystrokes down to menu +	return mMenu->handleUnicodeChar(uni_char, TRUE); +} + +BOOL LLTearOffMenu::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ +	if (!mMenu->getHighlightedItem()) +	{ +		if (key == KEY_UP) +		{ +			mMenu->highlightPrevItem(NULL);		 +			return TRUE; +		} +		else if (key == KEY_DOWN) +		{ +			mMenu->highlightNextItem(NULL); +			return TRUE; +		} +	} +	// pass keystrokes down to menu +	return mMenu->handleKey(key, mask, TRUE); +} + +void LLTearOffMenu::translate(S32 x, S32 y) +{ +	if (x != 0 && y != 0) +	{ +		// hide open sub-menus by clearing current hover item +		mMenu->clearHoverItem(); +	} +	LLFloater::translate(x, y); +} +  //static  LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)  { diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 7dcd950996..0dca8f2550 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -129,14 +129,17 @@ public:  	virtual void updateBranchParent( LLView* parentp ){};  	// doIt() - do the primary funcationality of the menu item. -	virtual void doIt( void ) = 0; +	virtual void doIt( void );  	// set the hover status (called by it's menu)  	virtual void setHighlight( BOOL highlight ); -	// determine if this object is active +	// determine if this represents an active sub-menu  	virtual BOOL isActive( void ) const; +	// determine if this represents an open sub-menu +	virtual BOOL isOpen( void ) const; +  	virtual void setEnabledSubMenus(BOOL enable){};  	// LLView Functionality @@ -144,6 +147,8 @@ public:  	virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );  	virtual void draw( void ); +	BOOL getHover() { return mGotHover; } +  	BOOL getDrawTextDisabled() const { return mDrawTextDisabled; }  protected: @@ -398,9 +403,9 @@ public:  	// LLView Functionality  	virtual BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); -	virtual BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ); +	//virtual BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ); +	virtual BOOL handleUnicodeCharHere( llwchar uni_char, BOOL called_from_parent );  	virtual BOOL handleHover( S32 x, S32 y, MASK mask ); -	virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );  	virtual void draw( void );  	virtual void drawBackground(LLMenuItemGL* itemp, LLColor4& color);  	virtual void setVisible(BOOL visible); @@ -409,7 +414,7 @@ public:  	LLMenuGL* getChildMenuByName(const LLString& name, BOOL recurse) const; -	BOOL clearHoverItem(BOOL include_active = TRUE); +	BOOL clearHoverItem();  	// return the name label  	const LLString& getLabel( void ) const { return mLabel.getString(); } @@ -445,7 +450,11 @@ public:  	// sets the left,bottom corner of menu, useful for popups  	void setLeftAndBottom(S32 left, S32 bottom); -	virtual void handleJumpKey(KEY key); +	virtual BOOL handleJumpKey(KEY key); + +	virtual BOOL jumpKeysActive(); + +	virtual BOOL isOpen();  	// Shape this menu to fit the current state of the children, and  	// adjust the child rects to fit. This is called automatically @@ -491,8 +500,10 @@ public:  	KEY getJumpKey() { return mJumpKey; }  	void setJumpKey(KEY key) { mJumpKey = key; } -	static void onFocusLost(LLView* old_focus); +	static void setKeyboardMode(BOOL mode) { sKeyboardMode = mode; } +	static BOOL getKeyboardMode() { return sKeyboardMode; } +	static void onFocusLost(LLView* old_focus);  	static LLView *sDefaultMenuContainer;  protected: @@ -501,6 +512,7 @@ protected:  protected:  	static LLColor4 sDefaultBackgroundColor; +	static BOOL		sKeyboardMode;  	LLColor4		mBackgroundColor;  	BOOL			mBgVisible; @@ -602,7 +614,8 @@ private:  class LLMenuBarGL : public LLMenuGL  {  protected: -	std::list <LLKeyBinding*> mAccelerators; +	std::list <LLKeyBinding*>	mAccelerators; +	BOOL						mAltKeyTrigger;  public:  	LLMenuBarGL( const LLString& name ); @@ -613,10 +626,15 @@ public:  	virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_BAR; }  	virtual LLString getWidgetTag() const { return LL_MENU_BAR_GL_TAG; } +	virtual BOOL handleAcceleratorKey(KEY key, MASK mask); +	virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); +	virtual BOOL handleJumpKey(KEY key); +  	// rearrange the child rects so they fit the shape of the menu  	// bar. -	virtual void handleJumpKey(KEY key);  	virtual void arrange( void ); +	virtual void draw(); +	virtual BOOL jumpKeysActive();  	// add a vertical separator to this menu  	virtual BOOL appendSeparator( const LLString &separator_name = "separator" ); @@ -629,6 +647,12 @@ public:  	// Returns x position of rightmost child, usually Help menu  	S32 getRightmostMenuEdge(); + +	void resetMenuTrigger() { mAltKeyTrigger = FALSE; } + +protected: +	void checkMenuTrigger(); +  };  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -679,6 +703,9 @@ public:  	virtual void draw(void);  	virtual void onFocusReceived();  	virtual void onFocusLost(); +	virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); +	virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); +	virtual void translate(S32 x, S32 y);  protected:  	LLTearOffMenu(LLMenuGL* menup); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 64ba319151..8c3d2362c5 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -894,7 +894,6 @@ BOOL LLView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_scre  	return handled;  } -  BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)  {  	BOOL handled = FALSE; @@ -908,17 +907,14 @@ BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)  		}  	} -	if( !handled ) +	// JC: Must pass to disabled views, since they could have +	// keyboard focus, which requires the escape key to exit. +	if (!handled && getVisible())  	{ -		// JC: Must pass to disabled views, since they could have -		// keyboard focus, which requires the escape key to exit. -		if (getVisible()) +		handled = handleKeyHere( key, mask, called_from_parent ); +		if (handled && LLView::sDebugKeys)  		{ -			handled = handleKeyHere( key, mask, called_from_parent ); -			if (handled && LLView::sDebugKeys) -			{ -				llinfos << "Key handled by " << getName() << llendl; -			} +			llinfos << "Key handled by " << getName() << llendl;  		}  	} @@ -945,25 +941,20 @@ BOOL LLView::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)  	return FALSE;  } -  BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)  {  	BOOL handled = FALSE; -	/*  	if( called_from_parent )  	{  		// Downward traversal  		if (getVisible() && mEnabled)  		{ -			handled = childrenHandleKey( key, mask ) != NULL; +			handled = childrenHandleUnicodeChar( uni_char ) != NULL;  		}  	} -	*/ -	// JC: Must pass to disabled views, since they could have -	// keyboard focus, which requires the escape key to exit. -	if (getVisible()) +	if (!handled && getVisible())  	{  		handled = handleUnicodeCharHere(uni_char, called_from_parent);  		if (handled && LLView::sDebugKeys) @@ -1215,6 +1206,30 @@ LLView* LLView::childrenHandleKey(KEY key, MASK mask)  	return handled_view;  } +// Called during downward traversal +LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char) +{ +	LLView* handled_view = NULL; + +	if ( getVisible() && mEnabled ) +	{ +		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) +		{ +			LLView* viewp = *child_it; +			if (viewp->handleUnicodeChar(uni_char, TRUE)) +			{ +				if (LLView::sDebugKeys) +				{ +					llinfos << "Unicode character handled by " << viewp->getName() << llendl; +				} +				handled_view = viewp; +				break; +			} +		} +	} + +	return handled_view; +}  LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)  { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 9a36c56e3e..839c300476 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -448,6 +448,7 @@ protected:  	virtual BOOL	handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);  	LLView*	childrenHandleKey(KEY key, MASK mask); +	LLView* childrenHandleUnicodeChar(llwchar uni_char);  	LLView*	childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,  											  BOOL drop,  											  EDragAndDropType type, diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index ee42f53571..a0970f0140 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -205,6 +205,8 @@ BOOL LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask  	{  		mKeyLevel[translated_key] = TRUE;  		mKeyLevelTimer[translated_key].reset(); +		mKeyLevelFrameCount[translated_key] = 0; +		mKeyRepeated[translated_key] = FALSE;  	}  	else  	{ @@ -226,7 +228,6 @@ BOOL LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask)  	if( mKeyLevel[translated_key] )  	{  		mKeyLevel[translated_key] = FALSE; -		mKeyLevelFrameCount[translated_key] = 0;  		// Only generate key up events if the key is thought to  		// be down.  This allows you to call resetKeys() in the @@ -234,7 +235,6 @@ BOOL LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask)  		// messages in the same frame.  This was causing the  		// sequence W<return> in chat to move agents forward. JC  		mKeyUp[translated_key] = TRUE; -		mKeyRepeated[translated_key] = FALSE;  		handled = mCallbacks->handleTranslatedKeyUp(translated_key, translated_mask);  	} @@ -260,27 +260,13 @@ void LLKeyboard::toggleInsertMode()  // Returns time in seconds since key was pressed.  F32 LLKeyboard::getKeyElapsedTime(KEY key)  { -	if( mKeyLevel[key] ) -	{ -		return mKeyLevelTimer[key].getElapsedTimeF32(); -	} -	else -	{ -		return 0.f; -	} +	return mKeyLevelTimer[key].getElapsedTimeF32();  }  // Returns time in frames since key was pressed.  S32 LLKeyboard::getKeyElapsedFrameCount(KEY key)  { -	if( mKeyLevel[key] ) -	{ -		return mKeyLevelFrameCount[key]; -	} -	else -	{ -		return 0; -	} +	return mKeyLevelFrameCount[key];  }  // static diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 0c47d117d0..1d7919f06b 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -58,8 +58,8 @@ public:  	void			resetKeys(); -	F32				getCurKeyElapsedTime()	{ return getKeyElapsedTime( mCurScanKey ); } -	F32				getCurKeyElapsedFrameCount()	{ return (F32)getKeyElapsedFrameCount( mCurScanKey ); } +	F32				getCurKeyElapsedTime()	{ return getKeyDown(mCurScanKey) ? getKeyElapsedTime( mCurScanKey ) : 0.f; } +	F32				getCurKeyElapsedFrameCount()	{ return getKeyDown(mCurScanKey) ? (F32)getKeyElapsedFrameCount( mCurScanKey ) : 0.f; }  	BOOL			getKeyDown(const KEY key) { return mKeyLevel[key]; }  	BOOL			getKeyRepeated(const KEY key) { return mKeyRepeated[key]; } @@ -92,9 +92,10 @@ public:  	void setNumpadDistinct(e_numpad_distinct val) { mNumpadDistinct = val; }  	void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; } -protected:  	F32				getKeyElapsedTime( KEY key );  // Returns time in seconds since key was pressed.  	S32				getKeyElapsedFrameCount( KEY key );  // Returns time in frames since key was pressed. + +protected:  	void 			addKeyName(KEY key, const LLString& name);  protected: diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp index ddc099418c..fe553a8230 100644 --- a/indra/llwindow/llkeyboardwin32.cpp +++ b/indra/llwindow/llkeyboardwin32.cpp @@ -146,29 +146,32 @@ void LLKeyboardWin32::resetMaskKeys()  } -void LLKeyboardWin32::setModifierKeyLevel( KEY key, BOOL new_state ) -{ -	if( mKeyLevel[key] != new_state ) -	{ -		mKeyLevelFrameCount[key] = 0; - -		if( new_state ) -		{ -			mKeyLevelTimer[key].reset(); -		} -		mKeyLevel[key] = new_state; -	} -} +//void LLKeyboardWin32::setModifierKeyLevel( KEY key, BOOL new_state ) +//{ +//	if( mKeyLevel[key] != new_state ) +//	{ +//		mKeyLevelFrameCount[key] = 0; +// +//		if( new_state ) +//		{ +//			mKeyLevelTimer[key].reset(); +//		} +//		mKeyLevel[key] = new_state; +//	} +//}  MASK LLKeyboardWin32::updateModifiers()  { +	//RN: this seems redundant, as we should have already received the appropriate +	// messages for the modifier keys +  	// Scan the modifier keys as of the last Windows key message  	// (keydown encoded in high order bit of short) -	setModifierKeyLevel( KEY_SHIFT, GetKeyState(VK_SHIFT) & 0x8000 ); -	setModifierKeyLevel( KEY_CONTROL, GetKeyState(VK_CONTROL) & 0x8000 ); -	setModifierKeyLevel( KEY_ALT, GetKeyState(VK_MENU) & 0x8000 ); -	setModifierKeyLevel( KEY_CAPSLOCK, GetKeyState(VK_CAPITAL) & 0x0001); // Low order bit carries the toggle state. +	//setModifierKeyLevel( KEY_SHIFT, GetKeyState(VK_SHIFT) & 0x8000 ); +	//setModifierKeyLevel( KEY_CONTROL, GetKeyState(VK_CONTROL) & 0x8000 ); +	//setModifierKeyLevel( KEY_ALT, GetKeyState(VK_MENU) & 0x8000 ); +	//setModifierKeyLevel( KEY_CAPSLOCK, GetKeyState(VK_CAPITAL) & 0x0001); // Low order bit carries the toggle state.  	// Get mask for keyboard events  	MASK mask = currentMask(FALSE);  	return mask; diff --git a/indra/llwindow/llkeyboardwin32.h b/indra/llwindow/llkeyboardwin32.h index e7eb4b9c1e..c8355bb7a1 100644 --- a/indra/llwindow/llkeyboardwin32.h +++ b/indra/llwindow/llkeyboardwin32.h @@ -31,7 +31,7 @@ public:  protected:  	MASK	updateModifiers(); -	void	setModifierKeyLevel( KEY key, BOOL new_state ); +	//void	setModifierKeyLevel( KEY key, BOOL new_state );  private:  	std::map<U16, KEY> mTranslateNumpadMap;  	std::map<KEY, U16> mInvTranslateNumpadMap; diff --git a/indra/newview/app_settings/keys.ini b/indra/newview/app_settings/keys.ini index f6232abb47..b7fc6f9286 100644 --- a/indra/newview/app_settings/keys.ini +++ b/indra/newview/app_settings/keys.ini @@ -129,6 +129,13 @@ THIRD_PERSON	DOWN	ALT			move_backward  THIRD_PERSON	PGUP	ALT			spin_over  THIRD_PERSON	PGDN	ALT			spin_under +THIRD_PERSON	A		ALT			spin_around_cw +THIRD_PERSON	D		ALT			spin_around_ccw +THIRD_PERSON	W		ALT			move_forward +THIRD_PERSON	S		ALT			move_backward +THIRD_PERSON	E		ALT			spin_over +THIRD_PERSON	C		ALT			spin_under +  THIRD_PERSON	PAD_LEFT	ALT			spin_around_cw  THIRD_PERSON	PAD_RIGHT	ALT			spin_around_ccw  THIRD_PERSON	PAD_UP		ALT			move_forward diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 208a14a6c6..e958bd2152 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -95,6 +95,7 @@ LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)  		mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);  		mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );  		mInputEditor->setCommitOnFocusLost( FALSE ); +		mInputEditor->setRevertOnEsc( FALSE );  		mInputEditor->setIgnoreTab(TRUE);  		mInputEditor->setPassDelete(TRUE);  	} diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 181a66c2ef..59147c54f8 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -42,7 +42,8 @@  #include "lldraghandle.h"  const F32 CONTEXT_CONE_IN_ALPHA = 0.0f; -const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f; +const F32 CONTEXT_CONE_OUT_ALPHA = 1.f; +const F32 CONTEXT_FADE_TIME = 0.08f;  //////////////////////////////////////////////////////////////////////////////  // @@ -607,11 +608,11 @@ void LLFloaterColorPicker::draw()  	if (gFocusMgr.childHasMouseCapture(mDragHandle))  	{ -		mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f)); +		mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME));  	}  	else  	{ -		mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f)); +		mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME));  	}  	mPipetteBtn->setEnabled(gToolMgr != NULL); diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 718ea894aa..865de53512 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -110,6 +110,7 @@ BOOL LLFloaterIMPanel::postBuild()  		mInputEditor->setKeystrokeCallback( onInputEditorKeystroke );  		mInputEditor->setCallbackUserData(this);  		mInputEditor->setCommitOnFocusLost( FALSE ); +		mInputEditor->setRevertOnEsc( FALSE );  		LLButton* profile_btn = LLUICtrlFactory::getButtonByName(this, "profile_btn");  		profile_btn->setClickedCallback(&LLFloaterIMPanel::onClickProfile, this); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 509f040c34..f1c4f0d918 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2340,19 +2340,25 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, boo  				open_notecard(  					lastitem->getUUID(),  					LLString("Note: ") + lastitem->getName(), -					show_keep_discard); +					show_keep_discard, +					LLUUID::null,  +					FALSE);  				break;  			case LLAssetType::AT_LANDMARK:  				open_landmark(  					lastitem->getUUID(),  					LLString("  ") + lastitem->getName(), -					show_keep_discard); +					show_keep_discard, +					LLUUID::null, +					FALSE);  				break;  			case LLAssetType::AT_TEXTURE:  				open_texture(  					lastitem->getUUID(),  					LLString("Texture: ") + lastitem->getName(), -					show_keep_discard); +					show_keep_discard, +					LLUUID::null,  +					FALSE);  				break;  			default:  				break; diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index 74c7ae6c18..57d34f672b 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -88,7 +88,9 @@ void LLManip::getManipNormal(LLViewerObject* object, EManipPart manip, LLVector3  		LLVector3 arrow_axis;  		getManipAxis(object, manip, arrow_axis); -		LLVector3 cross = arrow_axis % gCamera->getAtAxis(); +		LLVector3 origin_dir = grid_origin - gCamera->getOrigin(); +		origin_dir.normVec(); +		LLVector3 cross = arrow_axis % origin_dir;  		normal = cross % arrow_axis;  		normal.normVec();  	} diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index 5cc2d2e39f..82974da634 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -157,6 +157,16 @@ void LLPreview::onCommit()  	LLViewerInventoryItem* item = 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<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);  		BOOL has_sale_info = FALSE;  		LLSaleInfo sale_info; diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 106bfd0095..cb7679557e 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -67,8 +67,8 @@ static const S32 FOOTER_HEIGHT = 100;  static const S32 BORDER_PAD = HPAD;  static const S32 TEXTURE_INVENTORY_PADDING = 30;  static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f; -static const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f; - +static const F32 CONTEXT_CONE_OUT_ALPHA = 1.f; +static const F32 CONTEXT_FADE_TIME = 0.08f;  //static const char CURRENT_IMAGE_NAME[] = "Current Texture";  //static const char WHITE_IMAGE_NAME[] = "Blank Texture"; @@ -437,11 +437,11 @@ void LLFloaterTexturePicker::draw()  	if (gFocusMgr.childHasMouseCapture(mDragHandle))  	{ -		mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f)); +		mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME));  	}  	else  	{ -		mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f)); +		mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME));  	}  	updateImageStats(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 689dcee663..8ab2b52a50 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -8263,6 +8263,8 @@ BOOL LLViewerMenuHolderGL::hideMenus()  			gParcelMgr->deselectLand();	  		}  	} +	gMenuBarView->clearHoverItem(); +	gMenuBarView->resetMenuTrigger();  	return handled;  } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8920d09fa6..df396c93be 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2137,7 +2137,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)  	}  	// don't pass keys on to world when something in ui has focus -	return gFocusMgr.childHasKeyboardFocus(mRootView); +	return gFocusMgr.childHasKeyboardFocus(mRootView) || (gMenuBarView && gMenuBarView->getHighlightedItem());  } @@ -2154,6 +2154,12 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)  		return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));  	} +	// let menus handle navigation (jump) keys +	if (gMenuBarView && gMenuBarView->handleUnicodeChar(uni_char, TRUE)) +	{ +		return TRUE; +	} +  	// Traverses up the hierarchy  	LLView* keyboard_focus = gFocusMgr.getKeyboardFocus();  	if( keyboard_focus ) @@ -2665,7 +2671,8 @@ BOOL LLViewerWindow::handlePerFrameHover()  	if (gParcelMgr  		&& !LLFloaterLand::floaterVisible()  		&& !LLFloaterBuyLand::isOpen() -		&& (!gFloaterTools || !gFloaterTools->getVisible())) +		&& (!gFloaterTools || !gFloaterTools->getVisible()) +		&& !gToolMgr)  	{  		gParcelMgr->deselectLand();  	} @@ -3499,8 +3506,21 @@ BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, con  	mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y));  	LLVector3d	plane_normal_global_d;  	plane_normal_global_d.setVec(plane_normal_global); -	F64	mouse_look_at_scale = (plane_normal_global_d * (plane_point_global - gAgent.getCameraPositionGlobal())) -								/ (plane_normal_global_d * mouse_direction_global_d); +	F64 plane_mouse_dot = (plane_normal_global_d * mouse_direction_global_d); +	LLVector3d plane_origin_camera_rel = plane_point_global - gAgent.getCameraPositionGlobal(); +	F64	mouse_look_at_scale = (plane_normal_global_d * plane_origin_camera_rel) +								/ plane_mouse_dot; +	if (llabs(plane_mouse_dot) < 0.00001) +	{ +		// if mouse is parallel to plane, return closest point on line through plane origin +		// that is parallel to camera plane by scaling mouse direction vector +		// by distance to plane origin, modulated by deviation of mouse direction from plane origin +		LLVector3d plane_origin_dir = plane_origin_camera_rel; +		plane_origin_dir.normVec(); +		 +		mouse_look_at_scale = plane_origin_camera_rel.magVec() / (plane_origin_dir * mouse_direction_global_d); +	} +  	point = gAgent.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d;  	return mouse_look_at_scale > 0.0; | 
