diff options
Diffstat (limited to 'indra/llui')
| -rwxr-xr-x | indra/llui/lldockablefloater.cpp | 2 | ||||
| -rw-r--r-- | indra/llui/llfolderviewitem.cpp | 45 | ||||
| -rwxr-xr-x | indra/llui/llfolderviewitem.h | 4 | ||||
| -rwxr-xr-x | indra/llui/llfolderviewmodel.h | 2 | ||||
| -rwxr-xr-x | indra/llui/llmenugl.cpp | 58 | ||||
| -rwxr-xr-x | indra/llui/llmenugl.h | 5 | ||||
| -rwxr-xr-x | indra/llui/llsearcheditor.cpp | 13 | ||||
| -rwxr-xr-x | indra/llui/llsearcheditor.h | 2 | ||||
| -rwxr-xr-x | indra/llui/lltextbase.cpp | 53 | ||||
| -rwxr-xr-x | indra/llui/lltextbase.h | 8 | ||||
| -rwxr-xr-x | indra/llui/llurlentry.cpp | 88 | ||||
| -rwxr-xr-x | indra/llui/llurlentry.h | 24 | ||||
| -rwxr-xr-x | indra/llui/llurlregistry.cpp | 47 | ||||
| -rwxr-xr-x | indra/llui/tests/llurlentry_test.cpp | 172 | 
14 files changed, 343 insertions, 180 deletions
| diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index 3396213f1c..c937d190c6 100755 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -153,7 +153,7 @@ void LLDockableFloater::setVisible(BOOL visible)  		mDockControl.get()->repositionDockable();  	} -	if (visible) +	if (visible && !isMinimized())  	{  		LLFloater::setFrontmost(getAutoFocus());  	} diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 81a0204bc5..3def0386e1 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -129,6 +129,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)  	mSelectPending(FALSE),  	mLabelStyle( LLFontGL::NORMAL ),  	mHasVisibleChildren(FALSE), +	mIsFolderComplete(true),      mLocalIndentation(p.folder_indentation),  	mIndentation(0),  	mItemHeight(p.item_height), @@ -674,7 +675,7 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L  	//  	const S32 TOP_PAD = default_params.item_top_pad; -	if (hasVisibleChildren()) +	if (hasVisibleChildren() || !isFolderComplete())  	{  		LLUIImage* arrow_image = default_params.folder_arrow_image;  		gl_draw_scaled_rotated_image( @@ -934,6 +935,8 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ):  	mLastArrangeGeneration( -1 ),  	mLastCalculatedWidth(0)  { +	// folder might have children that are not loaded yet. Mark it as incomplete until chance to check it. +	mIsFolderComplete = false;  }  void LLFolderViewFolder::updateLabelRotation() @@ -1016,6 +1019,12 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height )  		mHasVisibleChildren = found;  	} +	if (!mIsFolderComplete) +	{ +		mIsFolderComplete = getFolderViewModel()->isFolderComplete(this); +	} + +  	// calculate height as a single item (without any children), and reshapes rectangle to match  	LLFolderViewItem::arrange( width, height ); @@ -1463,31 +1472,37 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)  	LLFolderView* root = getRoot(); -	for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end(); +	BOOL selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected + +	// array always go from 'will be selected' to ' will be unselected', iterate +	// in opposite direction to simplify identification of 'point of origin' in +	// case it is in the list we are working with +	for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_forward.rbegin(), end_it = items_to_select_forward.rend();  		it != end_it;  		++it)  	{  		LLFolderViewItem* item = *it; -		if (item->isSelected()) +		BOOL selected = item->isSelected(); +		if (!selection_reverse && selected)  		{ -			root->removeFromSelectionList(item); +			// it is our 'point of origin' where we shift/expand from +			// don't deselect it +			selection_reverse = TRUE;  		}  		else  		{ -			item->selectItem(); +			root->changeSelection(item, !selected);  		} -		root->addToSelectionList(item);  	} -	if (new_selection->isSelected()) +	if (selection_reverse)  	{ -		root->removeFromSelectionList(new_selection); +		// at some point we reversed selection, first element should be deselected +		root->changeSelection(last_selected_item_from_cur, FALSE);  	} -	else -	{ -		new_selection->selectItem(); -	} -	root->addToSelectionList(new_selection); + +	// element we expand to should always be selected +	root->changeSelection(new_selection, TRUE);  } @@ -1677,7 +1692,9 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r  	mIsOpen = openitem;  		if(!was_open && openitem)  		{ -		getViewModelItem()->openItem(); +			getViewModelItem()->openItem(); +			// openItem() will request content, it won't be incomplete +			mIsFolderComplete = true;  		}  		else if(was_open && !openitem)  		{ diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index ed4496cfaa..0322c8836d 100755 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -115,6 +115,7 @@ protected:  	F32							mControlLabelRotation;  	LLFolderView*				mRoot;  	bool						mHasVisibleChildren, +								mIsFolderComplete, // indicates that some children were not loaded/added yet  								mIsCurSelection,  								mDragAndDropTarget,  								mIsMouseOverTitle, @@ -212,6 +213,9 @@ public:  	BOOL hasVisibleChildren() { return mHasVisibleChildren; } +	// true if object can't have children +	BOOL isFolderComplete() { return mIsFolderComplete; } +  	// Call through to the viewed object and return true if it can be  	// removed. Returns true if it's removed.  	//virtual BOOL removeRecursively(BOOL single_item); diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index d60e36183b..a395af537a 100755 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -122,6 +122,7 @@ public:  	virtual void filter() = 0;  	virtual bool contentsReady() = 0; +	virtual bool isFolderComplete(class LLFolderViewFolder*) = 0;  	virtual void setFolderView(LLFolderView* folder_view) = 0;  	virtual LLFolderViewFilter& getFilter() = 0;  	virtual const LLFolderViewFilter& getFilter() const = 0; @@ -444,6 +445,7 @@ public:  	// By default, we assume the content is available. If a network fetch mechanism is implemented for the model,  	// this method needs to be overloaded and return the relevant fetch status.  	virtual bool contentsReady()					{ return true; } +	virtual bool isFolderComplete(LLFolderViewFolder* folder)					{ return true; }  	struct ViewModelCompare  	{ diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 7cdbcb0621..848367f8a8 100755 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -225,7 +225,6 @@ BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)  BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)  { -	setHover(TRUE);  	getWindow()->setCursor(UI_CURSOR_ARROW);  	return TRUE;  } @@ -236,6 +235,18 @@ BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleRightMouseDown(x,y,mask);  } +void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask) +{ +	setHover(TRUE); +	LLUICtrl::onMouseEnter(x,y,mask); +} + +void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) +{ +	setHover(FALSE); +	LLUICtrl::onMouseLeave(x,y,mask); +} +  //virtual  BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)  { @@ -533,9 +544,6 @@ void LLMenuItemGL::draw( void )  			gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);  		}  	} - -	// clear got hover every frame -	setHover(FALSE);  }  BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) @@ -1043,7 +1051,7 @@ void LLMenuItemBranchGL::onCommit( void )  	// keyboard navigation automatically propagates highlight to sub-menu  	// to facilitate fast menu control via jump keys -	if (LLMenuGL::getKeyboardMode() && getBranch()&& !getBranch()->getHighlightedItem()) +	if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem())  	{  		getBranch()->highlightNextItem(NULL);  	} @@ -1456,7 +1464,16 @@ BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )  {  	// switch to mouse control mode  	LLMenuGL::setKeyboardMode(FALSE); -	onCommit(); + +	if (getVisible() && isOpen()) +	{ +		LLMenuGL::sMenuContainer->hideMenus(); +	} +	else +	{ +		onCommit(); +	} +  	make_ui_sound("UISndClick");  	return TRUE;  } @@ -1588,10 +1605,6 @@ void LLMenuItemBranchDownGL::draw( void )  			gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);  		}  	} - -	// reset every frame so that we only show highlight  -	// when we get hover events on that frame -	setHover(FALSE);  } @@ -3381,6 +3394,11 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)  	return LLMenuGL::handleMouseDown(x, y, mask);  } +BOOL LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) +{ +	return LLMenuGL::handleMouseDown(x, y, mask); +} +  void LLMenuBarGL::draw()  {  	LLMenuItemGL* itemp = getHighlightedItem(); @@ -3625,8 +3643,24 @@ BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )  	BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;  	if (!handled)  	{ -		// clicked off of menu, hide them all -		hideMenus(); +		LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu(); +		LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL; +		if (parent_menu && parent_menu->getVisible()) +		{ +			// don't hide menu if parent was hit +			LLRect parent_rect; +			parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this); +			if (!parent_rect.pointInRect(x, y)) +			{ +				// clicked off of menu and parent, hide them all +				hideMenus(); +			} +		} +		else +		{ +			// no visible parent, clicked off of menu, hide them all +			hideMenus(); +		}  	}  	return handled;  } diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index ae9b169691..628dedb906 100755 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -158,6 +158,10 @@ public:  	virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );  	virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );  	virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + +	virtual void	onMouseEnter(S32 x, S32 y, MASK mask); +	virtual void	onMouseLeave(S32 x, S32 y, MASK mask); +  	virtual void draw( void );  	BOOL getHover() const { return mGotHover; } @@ -753,6 +757,7 @@ public:  	/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);  	/*virtual*/ BOOL handleJumpKey(KEY key);  	/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); +	/*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);  	/*virtual*/ void draw();  	/*virtual*/ BOOL jumpKeysActive(); diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index ea96fc573d..1fdd05a11c 100755 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -29,6 +29,7 @@  #include "linden_common.h"  #include "llsearcheditor.h" +#include "llkeyboard.h"  LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)  :	LLUICtrl(p), @@ -166,4 +167,16 @@ void LLSearchEditor::handleKeystroke()  	{  		mKeystrokeCallback(this, getValue());  	} + +	KEY key = gKeyboard->currentKey(); +	if (key == KEY_LEFT || +		key == KEY_RIGHT) +	{ +			return; +	} + +	if (mTextChangedCallback) +	{ +		mTextChangedCallback(this, getValue()); +	}  } diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h index c2d7916938..3b12868225 100755 --- a/indra/llui/llsearcheditor.h +++ b/indra/llui/llsearcheditor.h @@ -82,12 +82,14 @@ public:  	virtual void	setFocus( BOOL b );  	void			setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; } +	void			setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; }  protected:  	void onClearButtonClick(const LLSD& data);  	virtual void handleKeystroke();  	commit_callback_t mKeystrokeCallback; +	commit_callback_t mTextChangedCallback;  	LLLineEditor* mSearchEditor;  	LLButton* mSearchButton;  	LLButton* mClearButton; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 602a703450..bf660849c4 100755 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -37,6 +37,7 @@  #include "lltextparser.h"  #include "lltextutil.h"  #include "lltooltip.h" +#include "lltrans.h"  #include "lluictrl.h"  #include "llurlaction.h"  #include "llurlregistry.h" @@ -2060,27 +2061,33 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para  			// add icon before url if need  			LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted()); +			if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) +			{ +				setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); +			}  			// output the styled Url  			appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); +			bool tooltip_required =  !match.getTooltip().empty(); + +			// set the tooltip for the Url label +			if (tooltip_required) +			{ +				setLastSegmentToolTip(match.getTooltip()); +			} -			// show query part of url with gray color only for LLUrlEntryHTTP and LLUrlEntryHTTPNoProtocol url entries +			// show query part of url with gray color only for LLUrlEntryHTTP url entries  			std::string label = match.getQuery();  			if (label.size())  			{  				link_params.color = LLColor4::grey;  				link_params.readonly_color = LLColor4::grey;  				appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); -			} -			 -			// set the tooltip for the Url label -			if (! match.getTooltip().empty()) -			{ -				segment_set_t::iterator it = getSegIterContaining(getLength()-1); -				if (it != mSegments.end()) + +				// set the tooltip for the query part of url +				if (tooltip_required)  				{ -					LLTextSegmentPtr segment = *it; -					segment->setToolTip(match.getTooltip()); +					setLastSegmentToolTip(match.getTooltip());  				}  			} @@ -2107,6 +2114,16 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para  	}  } +void LLTextBase::setLastSegmentToolTip(const std::string &tooltip) +{ +	segment_set_t::iterator it = getSegIterContaining(getLength()-1); +	if (it != mSegments.end()) +	{ +		LLTextSegmentPtr segment = *it; +		segment->setToolTip(tooltip); +	} +} +  static LLTrace::BlockTimerStatHandle FTM_APPEND_TEXT("Append Text");  void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) @@ -3557,6 +3574,22 @@ S32	 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin  	return 0;  } +BOOL LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ +	if (!mTooltip.empty()) +	{ +		LLToolTipMgr::instance().show(mTooltip); +		return TRUE; +	} + +	return FALSE; +} + +void LLImageTextSegment::setToolTip(const std::string& tooltip) +{ +	mTooltip = tooltip; +} +  F32	LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)  {  	if ( (start >= 0) && (end <= mEnd - mStart)) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index dfc10923f3..87809aa8fb 100755 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -241,9 +241,15 @@ public:  	S32			getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;  	F32			draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); +	/*virtual*/ BOOL	handleToolTip(S32 x, S32 y, MASK mask); +	/*virtual*/ void	setToolTip(const std::string& tooltip); +  private:  	class LLTextBase&	mEditor;  	LLStyleConstSP	mStyle; + +protected: +	std::string		mTooltip;  };  typedef LLPointer<LLTextSegment> LLTextSegmentPtr; @@ -392,6 +398,8 @@ public:  	const	std::string& 	getLabel()	{ return mLabel.getString(); }  	const	LLWString&		getWlabel() { return mLabel.getWString();} +	void					setLastSegmentToolTip(const std::string &tooltip); +  	/**  	 * If label is set, draws text label (which is LLLabelTextSegment)  	 * that is visible when no user text provided diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 6db0d88998..7f6cc22e90 100755 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -203,10 +203,10 @@ std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const  {  	LLUriParser up(unescapeUrl(url)); -	std::string query; +	std::string label;  	up.extractParts(); -	up.glueSecond(query); - +	up.glueFirst(label); +	std::string query = url.substr(label.size());  	return query;  } @@ -229,7 +229,7 @@ static std::string getStringAfterToken(const std::string str, const std::string  LLUrlEntryHTTP::LLUrlEntryHTTP()  	: LLUrlEntryBase()  { -	mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", +	mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?\\.[a-z](:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_http.xml";  	mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -287,46 +287,6 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const  	return getUrlFromWikiLink(string);  } -// -// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com -// -LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() -	: LLUrlEntryBase() -{ -	mPattern = boost::regex("(" -				"\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR -				"|" // or -				"(?<!@)\\b[^[:space:]:@/>]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net -				")", -				boost::regex::perl|boost::regex::icase); -	mMenuName = "menu_url_http.xml"; -	mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ -	return urlToLabelWithGreyQuery(url); -} - -std::string LLUrlEntryHTTPNoProtocol::getQuery(const std::string &url) const -{ -	return urlToGreyQuery(url); -} - -std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const -{ -	if (string.find("://") == std::string::npos) -	{ -		return "http://" + escapeUrl(string); -	} -	return escapeUrl(string); -} - -std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const -{ -	return unescapeUrl(url); -} -  LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()  	: LLUrlEntryBase()  { @@ -489,7 +449,7 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const  //  LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL()  {                               -	mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?\\/\\S*", +	mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?\\/\\S*",  		boost::regex::perl|boost::regex::icase);  	mIcon = "Hand"; @@ -527,7 +487,7 @@ std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const  //  LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL()    { -	mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)", +	mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)",  		boost::regex::perl|boost::regex::icase);  	mIcon = "Hand"; @@ -1401,9 +1361,43 @@ std::string LLUrlEntryIcon::getIcon(const std::string &url)  	return mIcon;  } +// +// LLUrlEntryEmail Describes a generic mailto: Urls +// +LLUrlEntryEmail::LLUrlEntryEmail() +	: LLUrlEntryBase() +{ +	mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,6}", +							boost::regex::perl | boost::regex::icase); +	mMenuName = "menu_url_email.xml"; +	mTooltip = LLTrans::getString("TooltipEmail"); +} + +std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ +	int pos = url.find("mailto:"); + +	if (pos == std::string::npos) +	{ +		return escapeUrl(url); +	} + +	std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8)); +	return ret; +} + +std::string LLUrlEntryEmail::getUrl(const std::string &string) const +{ +	if (string.find("mailto:") == std::string::npos) +	{ +		return "mailto:" + escapeUrl(string); +	} +	return escapeUrl(string); +} +  LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile()  { -    mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/\\w+\\S*", +    mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/profile",          boost::regex::perl|boost::regex::icase);      mIcon = "Generic_Experience";  	mMenuName = "menu_url_experience.xml"; diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index dd1f257a3d..413c20a657 100755 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -157,19 +157,6 @@ public:  	/*virtual*/ std::string getUrl(const std::string &string) const;  }; -/// -/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com -/// -class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase -{ -public: -	LLUrlEntryHTTPNoProtocol(); -	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); -	/*virtual*/ std::string getQuery(const std::string &url) const; -	/*virtual*/ std::string getUrl(const std::string &string) const; -	/*virtual*/ std::string getTooltip(const std::string &url) const; -}; -  class LLUrlEntryInvalidSLURL : public LLUrlEntryBase  {  public: @@ -506,5 +493,16 @@ public:  	/*virtual*/ std::string getIcon(const std::string &url);  }; +/// +/// LLUrlEntryEmail Describes a generic mailto: Urls +/// +class LLUrlEntryEmail : public LLUrlEntryBase +{ +public: +	LLUrlEntryEmail(); +	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +	/*virtual*/ std::string getUrl(const std::string &string) const; +}; +  #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 2085505947..69eefa736c 100755 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -76,9 +76,7 @@ LLUrlRegistry::LLUrlRegistry()  	registerUrl(new LLUrlEntrySL());  	mUrlEntrySLLabel = new LLUrlEntrySLLabel();  	registerUrl(mUrlEntrySLLabel); -	// most common pattern is a URL without any protocol, -	// e.g., "secondlife.com" -	registerUrl(new LLUrlEntryHTTPNoProtocol());	 +	registerUrl(new LLUrlEntryEmail());  }  LLUrlRegistry::~LLUrlRegistry() @@ -155,11 +153,9 @@ static bool stringHasUrl(const std::string &text)  	return (text.find("://") != std::string::npos ||  			text.find("www.") != std::string::npos ||  			text.find(".com") != std::string::npos || -			text.find(".net") != std::string::npos || -			text.find(".edu") != std::string::npos || -			text.find(".org") != std::string::npos ||  			text.find("<nolink>") != std::string::npos || -			text.find("<icon") != std::string::npos); +			text.find("<icon") != std::string::npos || +			text.find("@") != std::string::npos);  }  bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted) @@ -218,9 +214,40 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL  	// did we find a match? if so, return its details in the match object  	if (match_entry)  	{ + +		// Skip if link is an email with an empty username (starting with @). See MAINT-5371. +		if (match_start > 0 && text.substr(match_start - 1, 1) == "@") +			return false; +  		// fill in the LLUrlMatch object and return it  		std::string url = text.substr(match_start, match_end - match_start + 1); +		LLUrlEntryBase *stripped_entry = NULL; +		if(LLStringUtil::containsNonprintable(url)) +		{ +			LLStringUtil::stripNonprintable(url); + +			std::vector<LLUrlEntryBase *>::iterator iter; +			for (iter = mUrlEntry.begin(); iter != mUrlEntry.end(); ++iter) +			{ +				LLUrlEntryBase *url_entry = *iter; +				U32 start = 0, end = 0; +				if (matchRegex(url.c_str(), url_entry->getPattern(), start, end)) +				{ +					if (mLLUrlEntryInvalidSLURL == *iter) +					{ +						if(url_entry && url_entry->isSLURLvalid(url)) +						{ +							continue; +						} +					} +					stripped_entry = url_entry; +					break; +				} +			} +		} + +  		if (match_entry == mUrlEntryTrusted)  		{  			LLUriParser up(url); @@ -228,10 +255,12 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL  			url = up.normalizedUri();  		} +		std::string url_label = stripped_entry? stripped_entry->getLabel(url, cb) : match_entry->getLabel(url, cb); +		std::string url_query = stripped_entry? stripped_entry->getQuery(url) : match_entry->getQuery(url);  		match.setValues(match_start, match_end,  						match_entry->getUrl(url), -						match_entry->getLabel(url, cb), -						match_entry->getQuery(url), +						url_label, +						url_query,  						match_entry->getTooltip(url),  						match_entry->getIcon(url),  						match_entry->getStyle(), diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 15f2354552..96e94c0f80 100755 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -653,79 +653,6 @@ namespace tut  	void object::test<11>()  	{  		// -		// test LLUrlEntryHTTPNoProtocol - general URLs without a protocol -		// -		LLUrlEntryHTTPNoProtocol url; - -		testRegex("naked .com URL", url, -				  "see google.com", -				  "http://google.com"); - -		testRegex("naked .org URL", url, -				  "see en.wikipedia.org for details", -				  "http://en.wikipedia.org"); - -		testRegex("naked .net URL", url, -				  "example.net", -				  "http://example.net"); - -		testRegex("naked .edu URL (2 instances)", url, -				  "MIT web site is at web.mit.edu and also www.mit.edu", -				  "http://web.mit.edu"); - -		testRegex("don't match e-mail addresses", url, -				  "test@lindenlab.com", -				  ""); - -		testRegex(".com URL with path", url, -				  "see secondlife.com/status for grid status", -				  "http://secondlife.com/status"); - -		testRegex(".com URL with port", url, -				  "secondlife.com:80", -				  "http://secondlife.com:80"); - -		testRegex(".com URL with port and path", url, -				  "see secondlife.com:80/status", -				  "http://secondlife.com:80/status"); - -		testRegex("www.*.com URL with port and path", url, -				  "see www.secondlife.com:80/status", -				  "http://www.secondlife.com:80/status"); - -		testRegex("invalid .com URL [1]", url, -				  "..com", -				  ""); - -		testRegex("invalid .com URL [2]", url, -				  "you.come", -				  ""); - -		testRegex("invalid .com URL [3]", url, -				  "recommended", -				  ""); - -		testRegex("invalid .edu URL", url, -				  "hi there scheduled maitenance has begun", -				  ""); - -		testRegex("invalid .net URL", url, -				  "foo.netty", -				  ""); - -		testRegex("XML tags around URL [1]", url, -				  "<foo>secondlife.com</foo>", -				  "http://secondlife.com"); - -		testRegex("XML tags around URL [2]", url, -				  "<foo>secondlife.com/status?bar=1</foo>", -				  "http://secondlife.com/status?bar=1"); -	} - -	template<> template<> -	void object::test<12>() -	{ -		//  		// test LLUrlEntryNoLink - turn off hyperlinking  		//  		LLUrlEntryNoLink url; @@ -752,7 +679,7 @@ namespace tut  	}  	template<> template<> -	void object::test<13>() +	void object::test<12>()  	{  		//  		// test LLUrlEntryRegion - secondlife:///app/region/<location> URLs @@ -860,4 +787,101 @@ namespace tut  			"secondlife:///app/region/Product%20Engine",  			"Product Engine");  	} + +	template<> template<> +	void object::test<13>() +	{ +		// +		// test LLUrlEntryemail - general emails +		// +		LLUrlEntryEmail url; + +		// Regex tests. +		testRegex("match e-mail addresses", url, +				  "test@lindenlab.com", +				  "mailto:test@lindenlab.com"); + +		testRegex("match e-mail addresses with mailto: prefix", url, +				  "mailto:test@lindenlab.com", +				  "mailto:test@lindenlab.com"); + +		testRegex("match e-mail addresses with different domains", url, +				  "test@foo.org.us", +				  "mailto:test@foo.org.us"); + +		testRegex("match e-mail addresses with different domains", url, +				  "test@foo.bar", +				  "mailto:test@foo.bar"); + +		testRegex("don't match incorrect e-mail addresses", url, +				  "test @foo.com", +				  ""); + +		testRegex("don't match incorrect e-mail addresses", url, +				  "test@ foo.com", +				  ""); +	} + +	template<> template<> +	void object::test<14>() +	{ +		// +		// test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com/* and http://*lindenlab.com/* urls +		// +		LLUrlEntrySecondlifeURL url; + +		testRegex("match urls with protocol", url, +				  "this url should match http://lindenlab.com/products/second-life", +				  "http://lindenlab.com/products/second-life"); + +		testRegex("match urls with protocol", url, +				  "search something https://marketplace.secondlife.com/products/search on marketplace and test the https", +				  "https://marketplace.secondlife.com/products/search"); + +		testRegex("match urls with port", url, +				  "let's specify some port http://secondlife.com:888/status", +				  "http://secondlife.com:888/status"); + +		testRegex("don't match urls w/o protocol", url, +				  "looks like an url something www.marketplace.secondlife.com/products but no https prefix", +				  ""); + +		testRegex("but with a protocol www is fine", url, +				  "so let's add a protocol http://www.marketplace.secondlife.com:8888/products", +				  "http://www.marketplace.secondlife.com:8888/products"); + +		testRegex("don't match urls w/o protocol", url, +				  "and even no www something secondlife.com/status", +				  ""); +	} + +	template<> template<> +	void object::test<15>() +	{ +		// +		// test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com and http://*lindenlab.com urls +		// + +		LLUrlEntrySimpleSecondlifeURL url; + +		testRegex("match urls with a protocol", url, +				  "this url should match http://lindenlab.com", +				  "http://lindenlab.com"); + +		testRegex("match urls with a protocol", url, +				  "search something https://marketplace.secondlife.com on marketplace and test the https", +				  "https://marketplace.secondlife.com"); + +		testRegex("don't match urls w/o protocol", url, +				  "looks like an url something www.marketplace.secondlife.com but no https prefix", +				  ""); + +		testRegex("but with a protocol www is fine", url, +				  "so let's add a protocol http://www.marketplace.secondlife.com", +				  "http://www.marketplace.secondlife.com"); + +		testRegex("don't match urls w/o protocol", url, +				  "and even no www something lindenlab.com", +				  ""); +	}  } | 
