diff options
Diffstat (limited to 'indra/llui')
65 files changed, 2821 insertions, 632 deletions
| diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index e98201ea63..33ab2e93b5 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -111,6 +111,7 @@ set(llui_SOURCE_FILES      llviewmodel.cpp      llview.cpp      llviewquery.cpp +    llwindowshade.cpp      )  set(llui_HEADER_FILES @@ -159,6 +160,7 @@ set(llui_HEADER_FILES      llnotificationslistener.h      llnotificationsutil.h      llnotificationtemplate.h +	llnotificationvisibilityrule.h      llpanel.h      llprogressbar.h      llradiogroup.h @@ -209,6 +211,7 @@ set(llui_HEADER_FILES      llviewmodel.h      llview.h      llviewquery.h +    llwindowshade.h      )  set_source_files_properties(${llui_HEADER_FILES} diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 179b32098a..9e4849c58b 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -203,7 +203,8 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()  	S32 width = getRect().getWidth();  	S32 height = getRect().getHeight(); -	gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get(),true); +	F32 alpha = getCurrentTransparency(); +	gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get() % alpha,true);  	LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());  	bool collapsible = (parent && parent->getCollapsible()); @@ -456,8 +457,7 @@ BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask)  	{  		if(y >= (getRect().getHeight() - HEADER_HEIGHT) )  		{ -			LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -			header->setFocus(true); +			mHeader->setFocus(true);  			changeOpenClose(getDisplayChildren());  			//reset stored state @@ -509,10 +509,9 @@ void LLAccordionCtrlTab::setAccordionView(LLView* panel)  std::string LLAccordionCtrlTab::getTitle() const  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		return header->getTitle(); +		return mHeader->getTitle();  	}  	else  	{ @@ -522,57 +521,51 @@ std::string LLAccordionCtrlTab::getTitle() const  void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl)  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		header->setTitle(title, hl); +		mHeader->setTitle(title, hl);  	}  }  void LLAccordionCtrlTab::setTitleFontStyle(std::string style)  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		header->setTitleFontStyle(style); +		mHeader->setTitleFontStyle(style);  	}  }  void LLAccordionCtrlTab::setTitleColor(LLUIColor color)  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		header->setTitleColor(color); +		mHeader->setTitleColor(color);  	}  }  boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		return header->setFocusReceivedCallback(cb); +		return mHeader->setFocusReceivedCallback(cb);  	}  	return boost::signals2::connection();  }  boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb)  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		return header->setFocusLostCallback(cb); +		return mHeader->setFocusLostCallback(cb);  	}  	return boost::signals2::connection();  }  void LLAccordionCtrlTab::setSelected(bool is_selected)  { -	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); -	if (header) +	if (mHeader)  	{ -		header->setSelected(is_selected); +		mHeader->setSelected(is_selected);  	}  } @@ -776,8 +769,7 @@ S32 LLAccordionCtrlTab::notify(const LLSD& info)  BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent)  { -	LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);	 -	if( !header->hasFocus() ) +	if( !mHeader->hasFocus() )  		return LLUICtrl::handleKey(key, mask, called_from_parent);  	if ( (key == KEY_RETURN )&& mask == MASK_NONE) @@ -830,15 +822,19 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent)  void LLAccordionCtrlTab::showAndFocusHeader()  { -	LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);	 -	header->setFocus(true); -	header->setSelected(mSelectionEnabled); +	mHeader->setFocus(true); +	mHeader->setSelected(mSelectionEnabled);  	LLRect screen_rc; -	LLRect selected_rc = header->getRect(); +	LLRect selected_rc = mHeader->getRect();  	localRectToScreen(selected_rc, &screen_rc); -	notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); +	// This call to notifyParent() is intended to deliver "scrollToShowRect" command +	// to the parent LLAccordionCtrl so by calling it from the direct parent of this +	// accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain +	// is shortened and messages from inside the collapsed tabs are avoided. +	// See STORM-536. +	getParent()->notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));  }  void    LLAccordionCtrlTab::storeOpenCloseState()  { diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 65ef3e5f8f..45ceaff696 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -98,7 +98,8 @@ LLButton::Params::Params()  	is_toggle("is_toggle", false),  	scale_image("scale_image", true),  	hover_glow_amount("hover_glow_amount"), -	commit_on_return("commit_on_return", true) +	commit_on_return("commit_on_return", true), +	use_draw_context_alpha("use_draw_context_alpha", true)  {  	addSynonym(is_toggle, "toggle");  	held_down_delay.seconds = 0.5f; @@ -158,7 +159,8 @@ LLButton::LLButton(const LLButton::Params& p)  	mLastDrawCharsCount(0),  	mMouseDownSignal(NULL),  	mMouseUpSignal(NULL), -	mHeldDownSignal(NULL) +	mHeldDownSignal(NULL), +	mUseDrawContextAlpha(p.use_draw_context_alpha)  {  	static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0); @@ -539,7 +541,7 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)  // virtual  void LLButton::draw()  { -	F32 alpha = getDrawContext().mAlpha; +	F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();  	bool flash = FALSE;  	static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0);  	static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 2d5fefa78c..16aa49b653 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -124,6 +124,8 @@ public:  		Optional<F32>				hover_glow_amount;  		Optional<TimeIntervalParam>	held_down_delay; +		Optional<bool>			use_draw_context_alpha; +  		Params();  	}; @@ -338,6 +340,8 @@ private:  	S32							mImageOverlayTopPad;  	S32							mImageOverlayBottomPad; +	bool						mUseDrawContextAlpha; +  	/*  	 * Space between image_overlay and label  	 */ diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index bbd8db2645..4fe444c1a4 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -88,27 +88,19 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)  		tbparams.font(p.font);  	}  	mLabel = LLUICtrlFactory::create<LLTextBox> (tbparams); +	mLabel->reshapeToFitText();  	addChild(mLabel); -	S32 text_width = mLabel->getTextBoundingRect().getWidth(); -	S32 text_height = llround(mFont->getLineHeight()); -	LLRect label_rect; -	label_rect.setOriginAndSize( -		llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing, -		llcheckboxctrl_vpad + 1, // padding to get better alignment -		text_width + llcheckboxctrl_hpad, -		text_height ); -	mLabel->setShape(label_rect); - +	LLRect label_rect = mLabel->getRect();  	// Button  	// Note: button cover the label by extending all the way to the right. -	LLRect btn_rect; +	LLRect btn_rect = p.check_button.rect();  	btn_rect.setOriginAndSize( -		llcheckboxctrl_hpad, -		llcheckboxctrl_vpad, -		llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width + llcheckboxctrl_hpad, -		llmax( text_height, llcheckboxctrl_btn_size() ) + llcheckboxctrl_vpad); +		btn_rect.mLeft, +		btn_rect.mBottom, +		llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft), +		llmax( label_rect.getHeight(), btn_rect.mTop));  	std::string active_true_id, active_false_id;  	std::string inactive_true_id, inactive_false_id; @@ -174,31 +166,20 @@ void LLCheckBoxCtrl::clear()  void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)  { -	//stretch or shrink bounding rectangle of label when rebuilding UI at new scale -	static LLUICachedControl<S32> llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0); -	static LLUICachedControl<S32> llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0); -	static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0); -	static LLUICachedControl<S32> llcheckboxctrl_btn_size ("UICheckboxctrlBtnSize", 0); -	S32 text_width = mLabel->getTextBoundingRect().getWidth(); -	S32 text_height = llround(mFont->getLineHeight()); -	LLRect label_rect; -	label_rect.setOriginAndSize( -		llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing, -		llcheckboxctrl_vpad, -		text_width, -		text_height ); -	mLabel->setShape(label_rect); - -	LLRect btn_rect; +	mLabel->reshapeToFitText(); + +	LLRect label_rect = mLabel->getRect(); + +	// Button +	// Note: button cover the label by extending all the way to the right. +	LLRect btn_rect = mButton->getRect();  	btn_rect.setOriginAndSize( -		llcheckboxctrl_hpad, -		llcheckboxctrl_vpad, -		llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width, -		llmax( text_height, llcheckboxctrl_btn_size() ) ); -	mButton->setShape( btn_rect ); -	 -	LLUICtrl::reshape(width, height, called_from_parent); +		btn_rect.mLeft, +		btn_rect.mBottom, +		llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft), +		llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight())); +	mButton->setShape(btn_rect);  }  //virtual diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index edd2cd340b..8b6a73af56 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -52,8 +52,6 @@  #include "lltooltip.h"  // Globals -S32 LLCOMBOBOX_HEIGHT = 0; -S32 LLCOMBOBOX_WIDTH = 0;  S32 MAX_COMBO_WIDTH = 500;  static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box"); @@ -96,6 +94,7 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)  	mMaxChars(p.max_chars),  	mPrearrangeCallback(p.prearrange_callback()),  	mTextEntryCallback(p.text_entry_callback()), +	mTextChangedCallback(p.text_changed_callback()),  	mListPosition(p.list_position),  	mLastSelectedIndex(-1),  	mLabel(p.label) @@ -486,7 +485,7 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)  		LLLineEditor::Params params = p.combo_editor;  		params.rect(text_entry_rect);  		params.default_text(LLStringUtil::null); -		params.max_length_bytes(mMaxChars); +		params.max_length.bytes(mMaxChars);  		params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));  		params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1));  		params.commit_on_focus_lost(false); @@ -705,10 +704,10 @@ void LLComboBox::onItemSelected(const LLSD& data)  		setLabel(getSelectedItemLabel());  		if (mAllowTextEntry) -		{ -			gFocusMgr.setKeyboardFocus(mTextEntry); -			mTextEntry->selectAll(); -		} +	{ +		gFocusMgr.setKeyboardFocus(mTextEntry); +		mTextEntry->selectAll(); +	}  	}  	// hiding the list reasserts the old value stored in the text editor/dropdown button  	hideList(); @@ -771,7 +770,8 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask)  			return FALSE;  		}  		// if selection has changed, pop open list -		else if (mList->getLastSelectedItem() != last_selected_item) +		else if (mList->getLastSelectedItem() != last_selected_item || +				(key == KEY_DOWN || key == KEY_UP) && !mList->isEmpty())  		{  			showList();  		} @@ -835,6 +835,10 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor)  			mList->deselectAllItems();  			mLastSelectedIndex = -1;  		} +		if (mTextChangedCallback != NULL) +		{ +			(mTextChangedCallback)(line_editor, LLSD()); +		}  		return;  	} @@ -879,6 +883,10 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor)  		// RN: presumably text entry  		updateSelection();  	} +	if (mTextChangedCallback != NULL) +	{ +		(mTextChangedCallback)(line_editor, LLSD()); +	}  }  void LLComboBox::updateSelection() diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index f369147ded..74d64269bd 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -43,9 +43,6 @@  class LLFontGL;  class LLViewBorder; -extern S32 LLCOMBOBOX_HEIGHT; -extern S32 LLCOMBOBOX_WIDTH; -  class LLComboBox  :	public LLUICtrl, public LLCtrlListInterface  { @@ -76,7 +73,8 @@ public:  											allow_new_values;  		Optional<S32>						max_chars;  		Optional<commit_callback_t> 		prearrange_callback, -											text_entry_callback; +											text_entry_callback, +											text_changed_callback;  		Optional<EPreferredPosition, PreferredPositionValues>	list_position; @@ -193,6 +191,7 @@ public:  	void			setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; }  	void			setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; } +	void			setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; }  	void			setButtonVisible(BOOL visible); @@ -223,9 +222,10 @@ private:  	BOOL				mTextEntryTentative;  	commit_callback_t	mPrearrangeCallback;  	commit_callback_t	mTextEntryCallback; +	commit_callback_t	mTextChangedCallback;  	commit_callback_t	mSelectionCallback; -        boost::signals2::connection mTopLostSignalConnection; -    S32					mLastSelectedIndex; +	boost::signals2::connection mTopLostSignalConnection; +	S32                 mLastSelectedIndex;  };  // A combo box with icons for the list of items. diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp index d48674f306..f6f5a0beb3 100644 --- a/indra/llui/lldockcontrol.cpp +++ b/indra/llui/lldockcontrol.cpp @@ -220,10 +220,15 @@ void LLDockControl::moveDockable()  	case TOP:  		x = dockRect.getCenterX() - dockableRect.getWidth() / 2;  		y = dockRect.mTop + dockableRect.getHeight(); -		// unique docking used with dock tongue, so add tongue height o the Y coordinate +		// unique docking used with dock tongue, so add tongue height to the Y coordinate  		if (use_tongue)  		{  			y += mDockTongue->getHeight(); + +			if ( y > rootRect.mTop) +			{ +				y = rootRect.mTop; +			}  		}  		// check is dockable inside root view rect @@ -257,7 +262,7 @@ void LLDockControl::moveDockable()  	case BOTTOM:  		x = dockRect.getCenterX() - dockableRect.getWidth() / 2;  		y = dockRect.mBottom; -		// unique docking used with dock tongue, so add tongue height o the Y coordinate +		// unique docking used with dock tongue, so add tongue height to the Y coordinate  		if (use_tongue)  		{  			y -= mDockTongue->getHeight(); @@ -292,9 +297,21 @@ void LLDockControl::moveDockable()  		break;  	} -	// move dockable -	dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), -			dockableRect.getHeight()); +	S32 max_available_height = rootRect.getHeight() - mDockTongueY - mDockTongue->getHeight(); + +	// A floater should be shrunk so it doesn't cover a part of its docking tongue and +	// there is a space between a dockable floater and a control to which it is docked. +	if (use_tongue && dockableRect.getHeight() >= max_available_height) +	{ +		dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), max_available_height); +		mDockableFloater->reshape(dockableRect.getWidth(), dockableRect.getHeight()); +	} +	else +	{ +		// move dockable +		dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), +				dockableRect.getHeight()); +	}  	LLRect localDocableParentRect;  	mDockableFloater->getParent()->screenRectToLocal(dockableRect,  			&localDocableParentRect); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index b758070419..c425782715 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1,4 +1,5 @@  /**  +   * @file llfloater.cpp   * @brief LLFloater base class   * @@ -61,7 +62,6 @@  // use this to control "jumping" behavior when Ctrl-Tabbing  const S32 TABBED_FLOATER_OFFSET = 0; -  std::string	LLFloater::sButtonNames[BUTTON_COUNT] =   {  	"llfloater_close_btn",		//BUTTON_CLOSE @@ -200,6 +200,21 @@ void LLFloater::initClass()  	{  		sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] );  	} + +	LLControlVariable* ctrl = LLUI::sSettingGroups["config"]->getControl("ActiveFloaterTransparency").get(); +	if (ctrl) +	{ +		ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); +		updateActiveFloaterTransparency(); +	} + +	ctrl = LLUI::sSettingGroups["config"]->getControl("InactiveFloaterTransparency").get(); +	if (ctrl) +	{ +		ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); +		updateInactiveFloaterTransparency(); +	} +  }  // defaults for floater param block pulled from widgets/floater.xml @@ -207,7 +222,7 @@ static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFl  LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)  :	LLPanel(),	// intentionally do not pass params here, see initFromParams -	mDragHandle(NULL), + 	mDragHandle(NULL),  	mTitle(p.title),  	mShortTitle(p.short_title),  	mSingleInstance(p.single_instance), @@ -257,9 +272,6 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)  	initFromParams(p); -	// chrome floaters don't take focus at all -	setFocusRoot(!getIsChrome()); -  	initFloater(p);  } @@ -347,6 +359,18 @@ void LLFloater::layoutDragHandle()  	updateTitleButtons();  } +// static +void LLFloater::updateActiveFloaterTransparency() +{ +	sActiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency"); +} + +// static +void LLFloater::updateInactiveFloaterTransparency() +{ +	sInactiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency"); +} +  void LLFloater::addResizeCtrls()  {	  	// Resize bars (sides) @@ -1163,6 +1187,7 @@ void LLFloater::setFocus( BOOL b )  			last_focus->setFocus(TRUE);  		}  	} +	updateTransparency(b ? TT_ACTIVE : TT_INACTIVE);  }  // virtual @@ -1435,6 +1460,9 @@ void LLFloater::setFrontmost(BOOL take_focus)  		// there are more than one floater view  		// so we need to query our parent directly  		((LLFloaterView*)getParent())->bringToFront(this, take_focus); + +		// Make sure to set the appropriate transparency type (STORM-732). +		updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE);  	}  } @@ -1622,7 +1650,8 @@ void	LLFloater::onClickCloseBtn()  // virtual  void LLFloater::draw()  { -	F32 alpha = getDrawContext().mAlpha; +	const F32 alpha = getCurrentTransparency(); +  	// draw background  	if( isBackgroundVisible() )  	{ @@ -1720,7 +1749,6 @@ void LLFloater::draw()  void	LLFloater::drawShadow(LLPanel* panel)  { -	F32 alpha = panel->getDrawContext().mAlpha;  	S32 left = LLPANEL_BORDER_WIDTH;  	S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH;  	S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; @@ -1737,10 +1765,32 @@ void	LLFloater::drawShadow(LLPanel* panel)  		shadow_color.mV[VALPHA] *= 0.5f;  	}  	gl_drop_shadow(left, top, right, bottom,  -		shadow_color % alpha,  +		shadow_color % getCurrentTransparency(),  		llround(shadow_offset));  } +void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) +{ +	child_list_t children = *view->getChildList(); +	child_list_t::iterator it = children.begin(); + +	LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view); +	if (ctrl) +	{ +		ctrl->setTransparencyType(transparency_type); +	} + +	for(; it != children.end(); ++it) +	{ +		updateTransparency(*it, transparency_type); +	} +} + +void LLFloater::updateTransparency(ETypeTransparency transparency_type) +{ +	updateTransparency(this, transparency_type); +} +  void	LLFloater::setCanMinimize(BOOL can_minimize)  {  	// if removing minimize/restore button programmatically, @@ -2857,7 +2907,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str  	params.from_xui = true;  	applyXUILayout(params, parent);   	initFromParams(params); -	 +  	initFloater(params);  	LLMultiFloater* last_host = LLFloater::getFloaterHost(); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 32d03f9f83..bb96272d02 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -284,6 +284,8 @@ public:  	static void		setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }  	static LLMultiFloater* getFloaterHost() {return sHostp; } + +	void			updateTransparency(ETypeTransparency transparency_type);  protected: @@ -341,6 +343,10 @@ private:  	void 			addDragHandle();  	void			layoutDragHandle();		// repair layout +	static void		updateActiveFloaterTransparency(); +	static void		updateInactiveFloaterTransparency(); +	void			updateTransparency(LLView* view, ETypeTransparency transparency_type); +  public:  	// Called when floater is opened, passes mKey  	// Public so external views or floaters can watch for this floater opening diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h index a43f095d67..8c000eee48 100644 --- a/indra/llui/llhandle.h +++ b/indra/llui/llhandle.h @@ -61,13 +61,6 @@ public:  		return *this;   	} -	template<typename Subclass> -	LLHandle<T>& operator =(const LLHandle<Subclass>& other)   -	{  -		mTombStone = other.mTombStone; -		return *this;  -	} -  	bool isDead() const   	{   		return mTombStone->getTarget() == NULL;  @@ -99,7 +92,6 @@ public:  	{  		return lhs.mTombStone > rhs.mTombStone;  	} -protected:  protected:  	LLPointer<LLTombStone<T> > mTombStone; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 627957061d..47f2cfaf89 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -41,6 +41,7 @@ static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon");  LLIconCtrl::Params::Params()  :	image("image_name"),  	color("color"), +	use_draw_context_alpha("use_draw_context_alpha", true),  	scale_image("scale_image")  {  	tab_stop = false; @@ -51,6 +52,7 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)  :	LLUICtrl(p),  	mColor(p.color()),  	mImagep(p.image), +	mUseDrawContextAlpha(p.use_draw_context_alpha),  	mPriority(0),  	mDrawWidth(0),  	mDrawHeight(0) @@ -71,7 +73,8 @@ void LLIconCtrl::draw()  {  	if( mImagep.notNull() )  	{ -		mImagep->draw(getLocalRect(), mColor.get() % getDrawContext().mAlpha ); +		const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); +		mImagep->draw(getLocalRect(), mColor.get() % alpha );  	}  	LLUICtrl::draw(); diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 79a8b0fb28..e9bdab2d47 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -48,6 +48,7 @@ public:  	{  		Optional<LLUIImage*>	image;  		Optional<LLUIColor>		color; +		Optional<bool>			use_draw_context_alpha;  		Ignored					scale_image;  		Params();  	}; @@ -79,6 +80,10 @@ protected:  	S32 mDrawWidth ;  	S32 mDrawHeight ; +	// If set to true (default), use the draw context transparency. +	// If false, will use transparency returned by getCurrentTransparency(). See STORM-698. +	bool mUseDrawContextAlpha; +  private:  	LLUIColor mColor;  	LLPointer<LLUIImage> mImagep; diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 940c7e7e18..19ac4c58a8 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -38,6 +38,12 @@  static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack");  static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel"); +void LLLayoutStack::OrientationNames::declareValues() +{ +	declare("horizontal", HORIZONTAL); +	declare("vertical", VERTICAL); +} +  //  // LLLayoutPanel  // @@ -47,47 +53,47 @@ LLLayoutPanel::LLLayoutPanel(const Params& p)   	mMaxDim(p.max_dim),    	mAutoResize(p.auto_resize),   	mUserResize(p.user_resize), -		mCollapsed(FALSE), -		mCollapseAmt(0.f), -		mVisibleAmt(1.f), // default to fully visible -		mResizeBar(NULL)  -	{ +	mCollapsed(FALSE), +	mCollapseAmt(0.f), +	mVisibleAmt(1.f), // default to fully visible +	mResizeBar(NULL)  +{  	// panels initialized as hidden should not start out partially visible  	if (!getVisible()) -		{ +	{  		mVisibleAmt = 0.f; -		} -		} +	} +}  void LLLayoutPanel::initFromParams(const Params& p) -		{ +{  	LLPanel::initFromParams(p);  	setFollowsNone(); -	} +}  LLLayoutPanel::~LLLayoutPanel() -	{ -		// probably not necessary, but... -		delete mResizeBar; -		mResizeBar = NULL; -	} +{ +	// probably not necessary, but... +	delete mResizeBar; +	mResizeBar = NULL; +}  F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation) -	{ +{  	if (orientation == LLLayoutStack::HORIZONTAL) -		{ -			F32 collapse_amt =  -			clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); -			return mVisibleAmt * collapse_amt; -		} -		else +	{ +		F32 collapse_amt =  +		clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); +		return mVisibleAmt * collapse_amt; +	} +	else  	{  			F32 collapse_amt =   			clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight())));  			return mVisibleAmt * collapse_amt; -		}  	} +}  //  // LLLayoutStack @@ -97,6 +103,8 @@ LLLayoutStack::Params::Params()  :	orientation("orientation"),  	animate("animate", true),  	clip("clip", true), +	open_time_constant("open_time_constant", 0.02f), +	close_time_constant("close_time_constant", 0.03f),  	border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0))  {  	name="stack"; @@ -107,10 +115,12 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)  	mMinWidth(0),  	mMinHeight(0),  	mPanelSpacing(p.border_size), -	mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL), +	mOrientation(p.orientation),  	mAnimate(p.animate),  	mAnimatedThisFrame(false), -	mClip(p.clip) +	mClip(p.clip), +	mOpenTimeConstant(p.open_time_constant), +	mCloseTimeConstant(p.close_time_constant)  {}  LLLayoutStack::~LLLayoutStack() @@ -303,9 +313,6 @@ void LLLayoutStack::updateLayout(BOOL force_resize)  	S32 total_width = 0;  	S32 total_height = 0; -	const F32 ANIM_OPEN_TIME = 0.02f; -	const F32 ANIM_CLOSE_TIME = 0.03f; -  	e_panel_list_t::iterator panel_it;  	for (panel_it = mPanels.begin(); panel_it != mPanels.end();	++panel_it)  	{ @@ -316,7 +323,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)  			{  				if (!mAnimatedThisFrame)  				{ -					(*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); +					(*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant));  					if ((*panel_it)->mVisibleAmt > 0.99f)  					{  						(*panel_it)->mVisibleAmt = 1.f; @@ -334,7 +341,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)  			{  				if (!mAnimatedThisFrame)  				{ -					(*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); +					(*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));  					if ((*panel_it)->mVisibleAmt < 0.001f)  					{  						(*panel_it)->mVisibleAmt = 0.f; @@ -349,11 +356,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize)  		if ((*panel_it)->mCollapsed)  		{ -			(*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); +			(*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));  		}  		else  		{ -			(*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); +			(*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));  		}  		if (mOrientation == HORIZONTAL) diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index e19ef403ef..4ac8ef0ee9 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -37,27 +37,35 @@ class LLLayoutPanel;  class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>  {  public: +	typedef enum e_layout_orientation +	{ +		HORIZONTAL, +		VERTICAL +	} ELayoutOrientation; + +	struct OrientationNames +	:	public LLInitParam::TypeValuesHelper<ELayoutOrientation, OrientationNames> +	{ +		static void declareValues(); +	}; +  	struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>  	{};  	struct Params : public LLInitParam::Block<Params, LLView::Params>  	{ -		Mandatory<std::string>	orientation; +		Mandatory<ELayoutOrientation, OrientationNames>	orientation;  		Optional<S32>			border_size;  		Optional<bool>			animate,  								clip; +		Optional<F32>			open_time_constant, +								close_time_constant;  		Params();  	};  	typedef LayoutStackRegistry child_registry_t; -	typedef enum e_layout_orientation -	{ -		HORIZONTAL, -		VERTICAL -	} ELayoutOrientation; -  	virtual ~LLLayoutStack();  	/*virtual*/ void draw(); @@ -137,6 +145,8 @@ private:  	bool mAnimatedThisFrame;  	bool mAnimate;  	bool mClip; +	F32 mOpenTimeConstant; +	F32 mCloseTimeConstant;  }; // end class LLLayoutStack  class LLLayoutPanel : public LLPanel @@ -167,6 +177,9 @@ public:  	~LLLayoutPanel();  	void initFromParams(const Params& p); +	void setMinDim(S32 value) { mMinDim = value; } +	void setMaxDim(S32 value) { mMaxDim = value; } +  protected:  	LLLayoutPanel(const Params& p)	; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index a1fc977ce1..7e348656a9 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -78,7 +78,7 @@ template class LLLineEditor* LLView::getChild<class LLLineEditor>(  //  LLLineEditor::Params::Params() -:	max_length_bytes("max_length", 254), +:	max_length(""),      keystroke_callback("keystroke_callback"),  	prevalidate_callback("prevalidate_callback"),  	background_image("background_image"), @@ -88,6 +88,7 @@ LLLineEditor::Params::Params()  	revert_on_esc("revert_on_esc", true),  	commit_on_focus_lost("commit_on_focus_lost", true),  	ignore_tab("ignore_tab", true), +	is_password("is_password", false),  	cursor_color("cursor_color"),  	text_color("text_color"),  	text_readonly_color("text_readonly_color"), @@ -108,7 +109,8 @@ LLLineEditor::Params::Params()  LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)  :	LLUICtrl(p), -	mMaxLengthBytes(p.max_length_bytes), +	mMaxLengthBytes(p.max_length.bytes), +	mMaxLengthChars(p.max_length.chars),  	mCursorPos( 0 ),  	mScrollHPos( 0 ),  	mTextPadLeft(p.text_pad_left), @@ -128,7 +130,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)  	mBorderThickness( 0 ),  	mIgnoreArrowKeys( FALSE ),  	mIgnoreTab( p.ignore_tab ), -	mDrawAsterixes( FALSE ), +	mDrawAsterixes( p.is_password ),  	mSelectAllonFocusReceived( p.select_on_focus ),  	mPassDelete(FALSE),  	mReadOnly(FALSE), @@ -313,6 +315,12 @@ void LLLineEditor::setMaxTextLength(S32 max_text_length)  	mMaxLengthBytes = max_len;  }  +void LLLineEditor::setMaxTextChars(S32 max_text_chars) +{ +	S32 max_chars = llmax(0, max_text_chars); +	mMaxLengthChars = max_chars; +}  +  void LLLineEditor::getTextPadding(S32 *left, S32 *right)  {  	*left = mTextPadLeft; @@ -358,6 +366,16 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)  	}  	mText.assign(truncated_utf8); +	if (mMaxLengthChars) +	{ +		LLWString truncated_wstring = utf8str_to_wstring(truncated_utf8); +		if (truncated_wstring.size() > (U32)mMaxLengthChars) +		{ +			truncated_wstring = truncated_wstring.substr(0, mMaxLengthChars); +		} +		mText.assign(wstring_to_utf8str(truncated_wstring)); +	} +  	if (all_selected)  	{  		// ...keep whole thing selected @@ -798,6 +816,7 @@ void LLLineEditor::addChar(const llwchar uni_char)  	}  	S32 cur_bytes = mText.getString().size(); +  	S32 new_bytes = wchar_utf8_length(new_c);  	BOOL allow_char = TRUE; @@ -807,6 +826,14 @@ void LLLineEditor::addChar(const llwchar uni_char)  	{  		allow_char = FALSE;  	} +	else if (mMaxLengthChars) +	{ +		S32 wide_chars = mText.getWString().size(); +		if ((wide_chars + 1) > mMaxLengthChars) +		{ +			allow_char = FALSE; +		} +	}  	if (allow_char)  	{ @@ -1107,7 +1134,19 @@ void LLLineEditor::pasteHelper(bool is_primary)  				clean_string = clean_string.substr(0, wchars_that_fit);  				LLUI::reportBadKeystroke();  			} -  + +			if (mMaxLengthChars) +			{ +				U32 available_chars = mMaxLengthChars - mText.getWString().size(); +		 +				if (available_chars < clean_string.size()) +				{ +					clean_string = clean_string.substr(0, available_chars); +				} + +				LLUI::reportBadKeystroke(); +			} +  			mText.insert(getCursor(), clean_string);  			setCursor( getCursor() + (S32)clean_string.length() );  			deselect(); @@ -1266,7 +1305,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  	// handle ctrl-uparrow if we have a history enabled line editor.  	case KEY_UP: -		if( mHaveHistory && ( MASK_CONTROL == mask ) ) +		if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) )  		{  			if( mCurrentHistoryLine > mLineHistory.begin() )  			{ @@ -1281,9 +1320,9 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  		}  		break; -	// handle ctrl-downarrow if we have a history enabled line editor +	// handle [ctrl]-downarrow if we have a history enabled line editor  	case KEY_DOWN: -		if( mHaveHistory  && ( MASK_CONTROL == mask ) ) +		if( mHaveHistory  && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) )  		{  			if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1 )  			{ @@ -1491,8 +1530,11 @@ void LLLineEditor::drawBackground()  	{  		image = mBgImage;  	} + +	if (!image) return; -	F32 alpha = getDrawContext().mAlpha; +	F32 alpha = getCurrentTransparency(); +  	// optionally draw programmatic border  	if (has_focus)  	{ diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 76d0187712..723423a5b9 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -59,11 +59,19 @@ public:  	typedef boost::function<void (LLLineEditor* caller)> keystroke_callback_t; +	struct MaxLength : public LLInitParam::Choice<MaxLength> +	{ +		Alternative<S32> bytes, chars; +		 +		MaxLength() : bytes("max_length_bytes", 254), +					  chars("max_length_chars", 0)  +		{} +	}; +  	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>  	{  		Optional<std::string>			default_text; -		Optional<S32>					max_length_bytes; - +		Optional<MaxLength>				max_length;  		Optional<keystroke_callback_t>	keystroke_callback;  		Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs>	prevalidate_callback; @@ -77,7 +85,8 @@ public:  		Optional<bool>					select_on_focus,  										revert_on_esc,  										commit_on_focus_lost, -										ignore_tab; +										ignore_tab, +										is_password;  		// colors  		Optional<LLUIColor>				cursor_color, @@ -214,6 +223,7 @@ public:  	void			setKeystrokeCallback(callback_t callback, void* user_data);  	void			setMaxTextLength(S32 max_text_length); +	void			setMaxTextChars(S32 max_text_chars);  	// Manipulate left and right padding for text  	void getTextPadding(S32 *left, S32 *right);  	void setTextPadding(S32 left, S32 right); @@ -277,6 +287,7 @@ protected:  	LLViewBorder* mBorder;  	const LLFontGL*	mGLFont;  	S32			mMaxLengthBytes;			// Max length of the UTF8 string in bytes +	S32			mMaxLengthChars;			// Maximum number of characters in the string  	S32			mCursorPos;					// I-beam is just after the mCursorPos-th character.  	S32			mScrollHPos;				// Horizontal offset from the start of mText.  Used for scrolling.  	LLFrameTimer mScrollTimer; @@ -326,7 +337,7 @@ protected:  	std::vector<S32> mPreeditPositions;  	LLPreeditor::standouts_t mPreeditStandouts; -	LLHandle<LLView> mContextMenuHandle; +	LLHandle<LLContextMenu> mContextMenuHandle;  private:  	// Instances that by default point to the statics but can be overidden in XML. diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 3df05f4d3f..eed0085273 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -29,7 +29,7 @@  #include "llmenubutton.h"  // Linden library includes -#include "llmenugl.h" +#include "lltoggleablemenu.h"  #include "llstring.h"  #include "v4color.h" @@ -44,58 +44,77 @@ LLMenuButton::Params::Params()  LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)  :	LLButton(p), -	mMenu(NULL), -	mMenuVisibleLastFrame(false) +	mIsMenuShown(false), +	mMenuPosition(MP_BOTTOM_LEFT)  {  	std::string menu_filename = p.menu_filename;  	if (!menu_filename.empty())  	{ -		mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); -		if (!mMenu) +		LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); +		if (!menu)  		{  			llwarns << "Error loading menu_button menu" << llendl; +			return;  		} + +		menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); + +		mMenuHandle = menu->getHandle(); + +		updateMenuOrigin();  	}  } -void LLMenuButton::toggleMenu() +boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb )  { -    if(!mMenu) -		return; +	return LLUICtrl::setMouseDownCallback(cb); +} -	if (mMenu->getVisible() || mMenuVisibleLastFrame) -	{ -		mMenu->setVisible(FALSE); -	} -	else +void LLMenuButton::hideMenu() +{ +	if(mMenuHandle.isDead()) return; + +	LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); +	if (menu)  	{ -	    LLRect rect = getRect(); -		//mMenu->needsArrange(); //so it recalculates the visible elements -		LLMenuGL::showPopup(getParent(), mMenu, rect.mLeft, rect.mBottom); +		menu->setVisible(FALSE);  	}  } - -void LLMenuButton::hideMenu()  -{  -	if(!mMenu) -		return; -	mMenu->setVisible(FALSE);  +LLToggleableMenu* LLMenuButton::getMenu() +{ +	return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());  } +void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/) +{ +	if (!menu) return; + +	mMenuHandle = menu->getHandle(); +	mMenuPosition = position; + +	menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); +}  BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )  { +	if (mMenuHandle.isDead()) return FALSE; +  	if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))  	{ +		// *HACK: We emit the mouse down signal to fire the callback bound to the +		// menu emerging event before actually displaying the menu. See STORM-263. +		LLUICtrl::handleMouseDown(-1, -1, MASK_NONE); +  		toggleMenu();  		return TRUE;  	} -	if (mMenu && mMenu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) +	LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); +	if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)  	{ -		mMenu->setVisible(FALSE); +		menu->setVisible(FALSE);  		return TRUE;  	} @@ -104,34 +123,84 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )  BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)  { -	if (hasTabStop() && !getIsChrome()) -	{ -		setFocus(TRUE); -	} +	LLButton::handleMouseDown(x, y, mask);  	toggleMenu(); -	if (getSoundFlags() & MOUSE_DOWN) -	{ -		make_ui_sound("UISndClick"); -	} -  	return TRUE;  } -void LLMenuButton::draw() +void LLMenuButton::toggleMenu()  { -	//we save this off so next frame when we try to close it by  -	//button click, and it hides menus before we get to it, we know -	mMenuVisibleLastFrame = mMenu && mMenu->getVisible(); -	 -	if (mMenuVisibleLastFrame) +	if(mMenuHandle.isDead()) return; + +	LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); +	if (!menu) return; + +	// Store the button rectangle to toggle menu visibility if a mouse event +	// occurred inside or outside the button rect. +	menu->setButtonRect(this); + +	if (!menu->toggleVisibility() && mIsMenuShown)  	{ +		setForcePressedState(false); +		mIsMenuShown = false; +	} +	else +	{ +		menu->buildDrawLabels(); +		menu->arrangeAndClear(); +		menu->updateParent(LLMenuGL::sMenuContainer); + +		updateMenuOrigin(); + +		LLMenuGL::showPopup(getParent(), menu, mX, mY); +  		setForcePressedState(true); +		mIsMenuShown = true;  	} +} -	LLButton::draw(); +void LLMenuButton::updateMenuOrigin() +{ +	if (mMenuHandle.isDead()) return; + +	LLRect rect = getRect(); -	setForcePressedState(false); +	switch (mMenuPosition) +	{ +		case MP_TOP_LEFT: +		{ +			mX = rect.mLeft; +			mY = rect.mTop + mMenuHandle.get()->getRect().getHeight(); +			break; +		} +		case MP_TOP_RIGHT: +		{ +			const LLRect& menu_rect = mMenuHandle.get()->getRect(); +			mX = rect.mRight - menu_rect.getWidth(); +			mY = rect.mTop + menu_rect.getHeight(); +			break; +		} +		case MP_BOTTOM_LEFT: +		{ +			mX = rect.mLeft; +			mY = rect.mBottom; +			break; +		} +	}  } +void LLMenuButton::onMenuVisibilityChange(const LLSD& param) +{ +	bool new_visibility = param["visibility"].asBoolean(); +	bool is_closed_by_button_click = param["closed_by_button_click"].asBoolean(); + +	// Reset the button "pressed" state only if the menu is shown by this particular +	// menu button (not any other control) and is not being closed by a click on the button. +	if (!new_visibility && !is_closed_by_button_click && mIsMenuShown) +	{ +		setForcePressedState(false); +		mIsMenuShown = false; +	} +} diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index 81ca0e047c..7b657595da 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -29,7 +29,7 @@  #include "llbutton.h" -class LLMenuGL; +class LLToggleableMenu;  class LLMenuButton  : public LLButton @@ -42,22 +42,42 @@ public:  		Optional<std::string>	menu_filename;  		Params(); -	};	 +	}; + +	typedef enum e_menu_position +	{ +		MP_TOP_LEFT, +		MP_TOP_RIGHT, +		MP_BOTTOM_LEFT +	} EMenuPosition; -	void toggleMenu(); -	/*virtual*/ void draw(); +	boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); +  	/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);  	/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); +  	void hideMenu(); -	LLMenuGL* getMenu() { return mMenu; } + +	LLToggleableMenu* getMenu(); +	void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT); + +	void setMenuPosition(EMenuPosition position) { mMenuPosition = position; }  protected:  	friend class LLUICtrlFactory;  	LLMenuButton(const Params&); +	void toggleMenu(); +	void updateMenuOrigin(); + +	void onMenuVisibilityChange(const LLSD& param); +  private: -	LLMenuGL*	mMenu; -	bool mMenuVisibleLastFrame; +	LLHandle<LLView>		mMenuHandle; +	bool					mIsMenuShown; +	EMenuPosition			mMenuPosition; +	S32						mX; +	S32						mY;  }; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 6d590cf54e..32d7be377a 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1462,7 +1462,7 @@ BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)  {  	BOOL branch_visible = getBranch()->getVisible();  	BOOL handled = getBranch()->handleAcceleratorKey(key, mask); -	if (handled && !branch_visible && getVisible()) +	if (handled && !branch_visible && isInVisibleChain())  	{  		// flash this menu entry because we triggered an invisible menu item  		LLMenuHolderGL::setActivatedItem(this); @@ -1637,6 +1637,10 @@ LLMenuScrollItem::LLMenuScrollItem(const Params& p)  	}  	LLButton::Params bparams; + +	// Disabled the Return key handling by LLMenuScrollItem instead of +	// passing the key press to the currently selected menu item. See STORM-385. +	bparams.commit_on_return(false);  	bparams.mouse_opaque(true);  	bparams.scale_image(false);  	bparams.click_callback(p.scroll_callback); @@ -1848,89 +1852,104 @@ BOOL LLMenuGL::isOpen()  	}  } -void LLMenuGL::scrollItemsUp() + + +bool LLMenuGL::scrollItems(EScrollingDirection direction)  { -	// Slowing down the items scrolling when arrow button is held  +	// Slowing down items scrolling when arrow button is held  	if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem)  	{  		mScrollItemsTimer.setTimerExpirySec(.033f);  	}  	else  	{ -		return; +		return false;  	} -	item_list_t::iterator cur_item_iter; -	item_list_t::iterator prev_item_iter; -	for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) +	switch (direction)  	{ -		if( (*cur_item_iter) == mFirstVisibleItem) +	case SD_UP: +	{ +		item_list_t::iterator cur_item_iter; +		item_list_t::iterator prev_item_iter; +		for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)  		{ -			break; +			if( (*cur_item_iter) == mFirstVisibleItem) +			{ +				break; +			} +			if ((*cur_item_iter)->getVisible()) +			{ +				prev_item_iter = cur_item_iter; +			}  		} -		if ((*cur_item_iter)->getVisible()) + +		if ((*prev_item_iter)->getVisible())  		{ -			prev_item_iter = cur_item_iter; +			mFirstVisibleItem = *prev_item_iter;  		} +		break;  	} - -	if ((*prev_item_iter)->getVisible()) +	case SD_DOWN:  	{ -		mFirstVisibleItem = *prev_item_iter; -	} -	 -	mNeedsArrange = TRUE; -	arrangeAndClear(); -} - -void LLMenuGL::scrollItemsDown() -{ -	// Slowing down the items scrolling when arrow button is held  -	if (mScrollItemsTimer.hasExpired()) -	{ -		mScrollItemsTimer.setTimerExpirySec(.033f); -	} -	else -	{ -		return; -	} -	 -	if (NULL == mFirstVisibleItem) -	{ -		mFirstVisibleItem = *mItems.begin(); -	} - -	item_list_t::iterator cur_item_iter; -	 -	for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) -	{ -		if( (*cur_item_iter) == mFirstVisibleItem) +		if (NULL == mFirstVisibleItem)  		{ -			break; +			mFirstVisibleItem = *mItems.begin();  		} -	} -	item_list_t::iterator next_item_iter; +		item_list_t::iterator cur_item_iter; -	if (cur_item_iter != mItems.end()) -	{ -		for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) +		for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)  		{ -			if( (*next_item_iter)->getVisible()) +			if( (*cur_item_iter) == mFirstVisibleItem)  			{  				break;  			}  		} -		 -		if (next_item_iter != mItems.end() && -		    (*next_item_iter)->getVisible()) + +		item_list_t::iterator next_item_iter; + +		if (cur_item_iter != mItems.end())  		{ -			mFirstVisibleItem = *next_item_iter; +			for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) +			{ +				if( (*next_item_iter)->getVisible()) +				{ +					break; +				} +			} + +			if (next_item_iter != mItems.end() && +				(*next_item_iter)->getVisible()) +			{ +				mFirstVisibleItem = *next_item_iter; +			}  		} +		break;  	} -	 +	case SD_BEGIN: +	{ +		mFirstVisibleItem = *mItems.begin(); +		break; +	} +	case SD_END: +	{ +		item_list_t::reverse_iterator first_visible_item_iter = mItems.rend(); + +		// Advance by mMaxScrollableItems back from the end of the list +		// to make the last item visible. +		std::advance(first_visible_item_iter, mMaxScrollableItems); +		mFirstVisibleItem = *first_visible_item_iter; +		break; +	} +	default: +		llwarns << "Unknown scrolling direction: " << direction << llendl; +	} +  	mNeedsArrange = TRUE;  	arrangeAndClear(); + +	return true;  }  // rearrange the child rects so they fit the shape of the menu. @@ -2162,7 +2181,7 @@ void LLMenuGL::arrange( void )  					LLMenuScrollItem::Params item_params;  					item_params.name(ARROW_UP);  					item_params.arrow_type(LLMenuScrollItem::ARROW_UP); -					item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItemsUp, this)); +					item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP));  					mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);  					LLUICtrl::addChild(mArrowUpItem); @@ -2173,7 +2192,7 @@ void LLMenuGL::arrange( void )  					LLMenuScrollItem::Params item_params;  					item_params.name(ARROW_DOWN);  					item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN); -					item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItemsDown, this)); +					item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN));  					mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);  					LLUICtrl::addChild(mArrowDownItem);				 @@ -2596,6 +2615,7 @@ LLMenuItemGL* LLMenuGL::getHighlightedItem()  LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled)  { +	if (mItems.empty()) return NULL;  	// highlighting first item on a torn off menu is the  	// same as giving focus to it  	if (!cur_item && getTornOff()) @@ -2603,14 +2623,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa  		((LLFloater*)getParent())->setFocus(TRUE);  	} -	item_list_t::iterator cur_item_iter; -	for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter) -	{ -		if( (*cur_item_iter) == cur_item) -		{ -			break; -		} -	} +	// Current item position in the items list +	item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item);  	item_list_t::iterator next_item_iter;  	if (cur_item_iter == mItems.end()) @@ -2621,9 +2635,37 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa  	{  		next_item_iter = cur_item_iter;  		next_item_iter++; + +		// First visible item position in the items list +		item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem); +  		if (next_item_iter == mItems.end())  		{  			next_item_iter = mItems.begin(); + +			// If current item is the last in the list, the menu is scrolled to the beginning +			// and the first item is highlighted. +			if (mScrollable && !scrollItems(SD_BEGIN)) +			{ +				return NULL; +			} +		} +		// If current item is the last visible, the menu is scrolled one item down +		// and the next item is highlighted. +		else if (mScrollable && +				 (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems) +		{ +			// Call highlightNextItem() recursively only if the menu was successfully scrolled down. +			// If scroll timer hasn't expired yet the menu won't be scrolled and calling +			// highlightNextItem() will result in an endless recursion. +			if (scrollItems(SD_DOWN)) +			{ +				return highlightNextItem(cur_item, skip_disabled); +			} +			else +			{ +				return NULL; +			}  		}  	} @@ -2674,6 +2716,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa  LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled)  { +	if (mItems.empty()) return NULL; +  	// highlighting first item on a torn off menu is the  	// same as giving focus to it  	if (!cur_item && getTornOff()) @@ -2681,14 +2725,8 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa  		((LLFloater*)getParent())->setFocus(TRUE);  	} -	item_list_t::reverse_iterator cur_item_iter; -	for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter) -	{ -		if( (*cur_item_iter) == cur_item) -		{ -			break; -		} -	} +	// Current item reverse position from the end of the list +	item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item);  	item_list_t::reverse_iterator prev_item_iter;  	if (cur_item_iter == mItems.rend()) @@ -2699,9 +2737,37 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa  	{  		prev_item_iter = cur_item_iter;  		prev_item_iter++; + +		// First visible item reverse position in the items list +		item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem); +  		if (prev_item_iter == mItems.rend())  		{  			prev_item_iter = mItems.rbegin(); + +			// If current item is the first in the list, the menu is scrolled to the end +			// and the last item is highlighted. +			if (mScrollable && !scrollItems(SD_END)) +			{ +				return NULL; +			} +		} +		// If current item is the first visible, the menu is scrolled one item up +		// and the previous item is highlighted. +		else if (mScrollable && +				 std::distance(first_visible_item_iter, cur_item_iter) <= 0) +		{ +			// Call highlightNextItem() only if the menu was successfully scrolled up. +			// If scroll timer hasn't expired yet the menu won't be scrolled and calling +			// highlightNextItem() will result in an endless recursion. +			if (scrollItems(SD_UP)) +			{ +				return highlightPrevItem(cur_item, skip_disabled); +			} +			else +			{ +				return NULL; +			}  		}  	} @@ -2872,12 +2938,12 @@ BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks )  	if( clicks > 0 )  	{  		while( clicks-- ) -			scrollItemsDown(); +			scrollItems(SD_DOWN);  	}  	else  	{  		while( clicks++ ) -			scrollItemsUp(); +			scrollItems(SD_UP);  	}  	return TRUE; @@ -2986,6 +3052,11 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)  	const S32 CURSOR_HEIGHT = 22;		// Approximate "normal" cursor size  	const S32 CURSOR_WIDTH = 12; +	if(menu->getChildList()->empty()) +	{ +		return; +	} +  	// Save click point for detecting cursor moves before mouse-up.  	// Must be in local coords to compare with mouseUp events.  	// If the mouse doesn't move, the menu will stay open ala the Mac. @@ -3066,7 +3137,10 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)  		mAltKeyTrigger = FALSE;  	} -	if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key)) +	if(!result  +		&& (key == KEY_F10 && mask == MASK_CONTROL)  +		&& !gKeyboard->getKeyRepeated(key) +		&& isInVisibleChain())  	{  		if (getHighlightedItem())  		{ @@ -3449,8 +3523,10 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)  			else  			{  				//highlight first enabled one -				pMenu->highlightNextItem(NULL); -				handled = true; +				if(pMenu->highlightNextItem(NULL)) +				{ +					handled = true; +				}  			}  		}  	} @@ -3683,9 +3759,7 @@ public:  	LLContextMenuBranch(const Params&);  	virtual ~LLContextMenuBranch() -	{ -		delete mBranch; -	} +	{}  	// called to rebuild the draw label  	virtual void	buildDrawLabel( void ); @@ -3693,21 +3767,21 @@ public:  	// onCommit() - do the primary funcationality of the menu item.  	virtual void	onCommit( void ); -	LLContextMenu*	getBranch() { return mBranch; } +	LLContextMenu*	getBranch() { return mBranch.get(); }  	void			setHighlight( BOOL highlight );  protected:  	void	showSubMenu(); -	LLContextMenu* mBranch; +	LLHandle<LLContextMenu> mBranch;  };  LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p)   :	LLMenuItemGL(p), -	mBranch( p.branch ) +	mBranch( p.branch()->getHandle() )  { -	mBranch->hide(); -	mBranch->setParentMenuItem(this); +	mBranch.get()->hide(); +	mBranch.get()->setParentMenuItem(this);  }  // called to rebuild the draw label @@ -3716,12 +3790,12 @@ void LLContextMenuBranch::buildDrawLabel( void )  	{  		// default enablement is this -- if any of the subitems are  		// enabled, this item is enabled. JC -		U32 sub_count = mBranch->getItemCount(); +		U32 sub_count = mBranch.get()->getItemCount();  		U32 i;  		BOOL any_enabled = FALSE;  		for (i = 0; i < sub_count; i++)  		{ -			LLMenuItemGL* item = mBranch->getItem(i); +			LLMenuItemGL* item = mBranch.get()->getItem(i);  			item->buildDrawLabel();  			if (item->getEnabled() && !item->getDrawTextDisabled() )  			{ @@ -3743,13 +3817,13 @@ void LLContextMenuBranch::buildDrawLabel( void )  void	LLContextMenuBranch::showSubMenu()  { -	LLMenuItemGL* menu_item = mBranch->getParentMenuItem(); +	LLMenuItemGL* menu_item = mBranch.get()->getParentMenuItem();  	if (menu_item != NULL && menu_item->getVisible())  	{  		S32 center_x;  		S32 center_y;  		localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); -		mBranch->show(center_x, center_y); +		mBranch.get()->show(center_x, center_y);  	}  } @@ -3769,7 +3843,7 @@ void LLContextMenuBranch::setHighlight( BOOL highlight )  	}  	else  	{ -		mBranch->hide(); +		mBranch.get()->hide();  	}  } @@ -3800,6 +3874,11 @@ void LLContextMenu::setVisible(BOOL visible)  // Takes cursor position in screen space?  void LLContextMenu::show(S32 x, S32 y)  { +	if (getChildList()->empty()) +	{ +		// nothing to show, so abort +		return; +	}  	// Save click point for detecting cursor moves before mouse-up.  	// Must be in local coords to compare with mouseUp events.  	// If the mouse doesn't move, the menu will stay open ala the Mac. diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 19b738312e..7bde8e83ec 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -397,6 +397,15 @@ public:  	static const std::string ARROW_UP;  	static const std::string ARROW_DOWN; +	// for scrollable menus +	typedef enum e_scrolling_direction +	{ +		SD_UP = 0, +		SD_DOWN = 1, +		SD_BEGIN = 2, +		SD_END = 3 +	} EScrollingDirection; +  protected:  	LLMenuGL(const LLMenuGL::Params& p);  	friend class LLUICtrlFactory; @@ -503,8 +512,7 @@ public:  	S32 getShortcutPad() { return mShortcutPad; } -	void scrollItemsUp(); -	void scrollItemsDown(); +	bool scrollItems(EScrollingDirection direction);  	BOOL isScrollable() const { return mScrollable; }  	static class LLMenuHolderGL* sMenuContainer; @@ -670,9 +678,12 @@ public:  			BOOL	appendContextSubMenu(LLContextMenu *menu); +			LLHandle<LLContextMenu> getHandle() { mHandle.bind(this); return mHandle; } +  protected: -	BOOL			mHoveredAnyItem; -	LLMenuItemGL*	mHoverItem; +	BOOL						mHoveredAnyItem; +	LLMenuItemGL*				mHoverItem; +	LLRootHandle<LLContextMenu>	mHandle;  }; diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index bd65625f53..91e5b6b9de 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -130,7 +130,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)  			params.name("MultiSliderCtrl Editor");  			params.rect(text_rect);  			params.font(p.font); -			params.max_length_bytes(MAX_STRING_LENGTH); +			params.max_length.bytes(MAX_STRING_LENGTH);  			params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit);  			params.prevalidate_callback(&LLTextValidate::validateFloat);  			params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 30cd85619e..cc9edfcdea 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -28,8 +28,11 @@  #include "llnotifications.h"  #include "llnotificationtemplate.h" +#include "llnotificationvisibilityrule.h" +#include "llavatarnamecache.h"  #include "llinstantmessage.h" +#include "llcachename.h"  #include "llxmlnode.h"  #include "lluictrl.h"  #include "lluictrlfactory.h" @@ -62,7 +65,7 @@ LLNotificationForm::FormElementBase::FormElementBase()  LLNotificationForm::FormIgnore::FormIgnore()  :	text("text"),  	control("control"), -	invert_control("invert_control", true), +	invert_control("invert_control", false),  	save_option("save_option", false)  {} @@ -79,7 +82,10 @@ LLNotificationForm::FormButton::FormButton()  LLNotificationForm::FormInput::FormInput()  :	type("type"), -	width("width", 0) +	text("text"), +	max_length_chars("max_length_chars"), +	width("width", 0), +	value("value")  {}  LLNotificationForm::FormElement::FormElement() @@ -133,12 +139,6 @@ private:  bool filterIgnoredNotifications(LLNotificationPtr notification)  { -	// filter everything if we are to ignore ALL -	if(LLNotifications::instance().getIgnoreAllNotifications()) -	{ -		return false; -	} -  	LLNotificationFormPtr form = notification->getForm();  	// Check to see if the user wants to ignore this alert  	return !notification->getForm()->getIgnored(); @@ -173,6 +173,28 @@ bool handleIgnoredNotification(const LLSD& payload)  	return false;  } +bool defaultResponse(const LLSD& payload) +{ +	if (payload["sigtype"].asString() == "add") +	{ +		LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); +		if (pNotif)  +		{ +			// supply default response +			pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON)); +		} +	} +	return false; +} + +bool visibilityRuleMached(const LLSD& payload) +{ +	// This is needed because LLNotifications::isVisibleByRules may have cancelled the notification. +	// Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order. +	return true; +} + +  namespace LLNotificationFilters  {  	// a sample filter @@ -190,7 +212,7 @@ LLNotificationForm::LLNotificationForm()  LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p)   :	mIgnore(IGNORE_NO), -	mInvertSetting(true) // ignore settings by default mean true=show, false=ignore +	mInvertSetting(false) // ignore settings by default mean true=show, false=ignore  {  	if (p.ignore.isProvided())  	{ @@ -215,7 +237,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica  		}  		else  		{ -			LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE); +			LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", TRUE);  			mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name);  		}  	} @@ -353,15 +375,15 @@ LLControlVariablePtr LLNotificationForm::getIgnoreSetting()  bool LLNotificationForm::getIgnored()  { -	bool ignored = false; +	bool show = true;  	if (mIgnore != LLNotificationForm::IGNORE_NO  		&& mIgnoreSetting)   	{ -		ignored = mIgnoreSetting->getValue().asBoolean(); -		if (mInvertSetting) ignored = !ignored; +		show = mIgnoreSetting->getValue().asBoolean(); +		if (mInvertSetting) show = !show;  	} -	return ignored; +	return !show;  }  void LLNotificationForm::setIgnored(bool ignored) @@ -369,7 +391,7 @@ void LLNotificationForm::setIgnored(bool ignored)  	if (mIgnoreSetting)  	{  		if (mInvertSetting) ignored = !ignored; -		mIgnoreSetting->setValue(ignored); +		mIgnoreSetting->setValue(!ignored);  	}  } @@ -400,12 +422,49 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par  		it != end_it;  		++it)  	{ -		mUniqueContext.push_back(it->key); +		mUniqueContext.push_back(it->value); +	} +	 +	lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl; +	 +	for(LLInitParam::ParamIterator<LLNotificationTemplate::Tag>::const_iterator it = p.tags.begin(), +			end_it = p.tags.end(); +		it != end_it; +		++it) +	{ +		lldebugs << "    tag \"" << std::string(it->value) << "\"" << llendl; +		mTags.push_back(it->value);  	}  	mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));  } +LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p) +{ +	if (p.show.isChosen()) +	{ +		mType = p.show.type; +		mTag = p.show.tag; +		mName = p.show.name; +		mVisible = true; +	} +	else if (p.hide.isChosen()) +	{ +		mType = p.hide.type; +		mTag = p.hide.tag; +		mName = p.hide.name; +		mVisible = false; +	} +	else if (p.respond.isChosen()) +	{ +		mType = p.respond.type; +		mTag = p.respond.tag; +		mName = p.respond.name; +		mVisible = false; +		mResponse = p.respond.response; +	} +} +  LLNotification::LLNotification(const LLNotification::Params& p) :   	mTimestamp(p.time_stamp),   	mSubstitutions(p.substitutions), @@ -675,6 +734,25 @@ bool LLNotification::hasUniquenessConstraints() const  	return (mTemplatep ? mTemplatep->mUnique : false);  } +bool LLNotification::matchesTag(const std::string& tag) +{ +	bool result = false; +	 +	if(mTemplatep) +	{ +		std::list<std::string>::iterator it; +		for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++) +		{ +			if((*it) == tag) +			{ +				result = true; +				break; +			} +		} +	} +	 +	return result; +}  void LLNotification::setIgnored(bool ignore)  { @@ -715,13 +793,19 @@ bool LLNotification::isEquivalentTo(LLNotificationPtr that) const  	{  		const LLSD& these_substitutions = this->getSubstitutions();  		const LLSD& those_substitutions = that->getSubstitutions(); +		const LLSD& this_payload = this->getPayload(); +		const LLSD& that_payload = that->getPayload();  		// highlander bit sez there can only be one of these  		for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();  			it != end_it;  			++it)  		{ -			if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()) +			// if templates differ in either substitution strings or payload with the given field name +			// then they are considered inequivalent +			// use of get() avoids converting the LLSD value to a map as the [] operator would +			if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString() +				|| this_payload.get(*it).asString() != that_payload.get(*it).asString())  			{  				return false;  			} @@ -1060,12 +1144,12 @@ std::string LLNotificationChannel::summarize()  // LLNotifications implementation  // ---  LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, -							       LLNotificationComparators::orderByUUID()), -				     mIgnoreAllNotifications(false) +															   LLNotificationComparators::orderByUUID()), +									mIgnoreAllNotifications(false)  {  	LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); -	 -	mListener.reset(new LLNotificationsListener(*this)); + +    mListener.reset(new LLNotificationsListener(*this));  } @@ -1180,6 +1264,7 @@ LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelN  void LLNotifications::initSingleton()  {  	loadTemplates(); +	loadVisibilityRules();  	createDefaultChannels();  } @@ -1187,15 +1272,19 @@ void LLNotifications::createDefaultChannels()  {  	// now construct the various channels AFTER loading the notifications,  	// because the history channel is going to rewrite the stored notifications file -	LLNotificationChannel::buildChannel("Expiration", "", +	LLNotificationChannel::buildChannel("Enabled", "", +		!boost::bind(&LLNotifications::getIgnoreAllNotifications, this)); +	LLNotificationChannel::buildChannel("Expiration", "Enabled",  		boost::bind(&LLNotifications::expirationFilter, this, _1)); -	LLNotificationChannel::buildChannel("Unexpired", "", +	LLNotificationChannel::buildChannel("Unexpired", "Enabled",  		!boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind  	LLNotificationChannel::buildChannel("Unique", "Unexpired",  		boost::bind(&LLNotifications::uniqueFilter, this, _1));  	LLNotificationChannel::buildChannel("Ignore", "Unique",  		filterIgnoredNotifications); -	LLNotificationChannel::buildChannel("Visible", "Ignore", +	LLNotificationChannel::buildChannel("VisibilityRules", "Ignore", +		boost::bind(&LLNotifications::isVisibleByRules, this, _1)); +	LLNotificationChannel::buildChannel("Visible", "VisibilityRules",  		&LLNotificationFilters::includeEverything);  	// create special persistent notification channel @@ -1203,6 +1292,8 @@ void LLNotifications::createDefaultChannels()  	new LLPersistentNotificationChannel();  	// connect action methods to these channels +	LLNotifications::instance().getChannel("Enabled")-> +		connectFailedFilter(&defaultResponse);  	LLNotifications::instance().getChannel("Expiration")->          connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));  	// uniqueHandler slot should be added as first slot of the signal due to @@ -1214,6 +1305,8 @@ void LLNotifications::createDefaultChannels()  //        connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));  	LLNotifications::instance().getChannel("Ignore")->  		connectFailedFilter(&handleIgnoredNotification); +	LLNotifications::instance().getChannel("VisibilityRules")-> +		connectFailedFilter(&visibilityRuleMached);  }  bool LLNotifications::addTemplate(const std::string &name,  @@ -1274,7 +1367,6 @@ LLNotifications::TemplateNames LLNotifications::getTemplateNames() const  typedef std::map<std::string, std::string> StringMap;  void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)  { -	//llwarns << "replaceSubstitutionStrings" << llendl;  	// walk the list of attributes looking for replacements  	for (LLXMLAttribList::iterator it=node->mAttributes.begin();  		 it != node->mAttributes.end(); ++it) @@ -1288,13 +1380,12 @@ void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)  			if (found != replacements.end())  			{  				replacement = found->second; -				//llwarns << "replaceSubstituionStrings: value: " << value << " repl: " << replacement << llendl; - +				lldebugs << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << llendl;  				it->second->setValue(replacement);  			}  			else  			{ -				llwarns << "replaceSubstituionStrings FAILURE: value: " << value << " repl: " << replacement << llendl; +				llwarns << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << llendl;  			}  		}  	} @@ -1343,6 +1434,12 @@ bool LLNotifications::loadTemplates()  	LLXUIParser parser;  	parser.readXUI(root, params, full_filename); +	if(!params.validateBlock()) +	{ +		llerrs << "Problem reading UI Notifications file: " << full_filename << llendl; +		return false; +	} +  	mTemplates.clear();  	for(LLInitParam::ParamIterator<LLNotificationTemplate::GlobalString>::const_iterator it = params.strings.begin(), end_it = params.strings.end(); @@ -1392,6 +1489,34 @@ bool LLNotifications::loadTemplates()  	return true;  } +bool LLNotifications::loadVisibilityRules() +{ +	const std::string xml_filename = "notification_visibility.xml"; +	std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename); + +	LLNotificationVisibilityRule::Rules params; +	LLSimpleXUIParser parser; +	parser.readXUI(full_filename, params); + +	if(!params.validateBlock()) +	{ +		llerrs << "Problem reading UI Notification Visibility Rules file: " << full_filename << llendl; +		return false; +	} + +	mVisibilityRules.clear(); + +	for(LLInitParam::ParamIterator<LLNotificationVisibilityRule::Rule>::iterator it = params.rules.begin(),  +			end_it = params.rules.end(); +		it != end_it; +		++it) +	{ +		mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(*it))); +	} + +	return true; +} +  // Add a simple notification (from XUI)  void LLNotifications::addFromCallback(const LLSD& name)  { @@ -1542,6 +1667,94 @@ bool LLNotifications::getIgnoreAllNotifications()  	return mIgnoreAllNotifications;   } +bool LLNotifications::isVisibleByRules(LLNotificationPtr n) +{ +	if(n->isRespondedTo()) +	{ +		// This avoids infinite recursion in the case where the filter calls respond() +		return true; +	} +	 +	VisibilityRuleList::iterator it; +	 +	for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++) +	{ +		// An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule. + +		lldebugs  +			<< "notification \"" << n->getName() << "\" "  +			<< "testing against " << ((*it)->mVisible?"show":"hide") << " rule, " +			<< "name = \"" << (*it)->mName << "\" " +			<< "tag = \"" << (*it)->mTag << "\" " +			<< "type = \"" << (*it)->mType << "\" " +			<< llendl; + +		if(!(*it)->mType.empty()) +		{ +			if((*it)->mType != n->getType()) +			{ +				// Type doesn't match, so skip this rule. +				continue; +			} +		} +		 +		if(!(*it)->mTag.empty()) +		{ +			// check this notification's tag(s) against it->mTag and continue if no match is found. +			if(!n->matchesTag((*it)->mTag)) +			{ +				// This rule's non-empty tag didn't match one of the notification's tags.  Skip this rule. +				continue; +			} +		} + +		if(!(*it)->mName.empty()) +		{ +			// check this notification's name against the notification's name and continue if no match is found. +			if((*it)->mName != n->getName()) +			{ +				// This rule's non-empty name didn't match the notification.  Skip this rule. +				continue; +			} +		} +		 +		// If we got here, the rule matches.  Don't evaluate subsequent rules. +		if(!(*it)->mVisible) +		{ +			// This notification is being hidden. +			 +			if((*it)->mResponse.empty()) +			{ +				// Response property is empty.  Cancel this notification. +				lldebugs << "cancelling notification " << n->getName() << llendl; + +				n->cancel(); +			} +			else +			{ +				// Response property is not empty.  Return the specified response. +				LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON); +				// TODO: verify that the response template has an item with the correct name +				response[(*it)->mResponse] = true; + +				lldebugs << "responding to notification " << n->getName() << " with response = " << response << llendl; +				 +				n->respond(response); +			} + +			return false; +		} +		 +		// If we got here, exit the loop and return true. +		break; +	} +	 +	lldebugs << "allowing notification " << n->getName() << llendl; + +	return true; +} +			 +  // ---  // END OF LLNotifications implementation  // ========================================================= @@ -1552,17 +1765,50 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification)  	return s;  } -void LLPostponedNotification::onCachedNameReceived(const LLUUID& id, const std::string& first, -		const std::string& last, bool is_group) +//static +void LLPostponedNotification::lookupName(LLPostponedNotification* thiz, +										 const LLUUID& id, +										 bool is_group)  { -	mName = first + " " + last; +	if (is_group) +	{ +		gCacheName->getGroup(id, +			boost::bind(&LLPostponedNotification::onGroupNameCache, +				thiz, _1, _2, _3)); +	} +	else +	{ +		LLAvatarNameCache::get(id, +			boost::bind(&LLPostponedNotification::onAvatarNameCache, +				thiz, _1, _2)); +	} +} -	LLStringUtil::trim(mName); -	if (mName.empty()) +void LLPostponedNotification::onGroupNameCache(const LLUUID& id, +											   const std::string& full_name, +											   bool is_group) +{ +	finalizeName(full_name); +} + +void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id, +												const LLAvatarName& av_name) +{ +	std::string name = av_name.getCompleteName(); + +	// from PE merge - we should figure out if this is the right thing to do +	if (name.empty())  	{ -		llwarns << "Empty name received for Id: " << id << llendl; -		mName = SYSTEM_FROM; +		llwarns << "Empty name received for Id: " << agent_id << llendl; +		name = SYSTEM_FROM;  	} +	 +	finalizeName(name); +} + +void LLPostponedNotification::finalizeName(const std::string& name) +{ +	mName = name;  	modifyNotificationParams();  	LLNotifications::instance().add(mParams);  	cleanup(); diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index f075c44520..34d3537781 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -98,8 +98,8 @@  #include "llinitparam.h"  #include "llnotificationslistener.h"  #include "llnotificationptr.h" -#include "llcachename.h" +class LLAvatarName;  typedef enum e_notification_priority  {  	NOTIFICATION_PRIORITY_UNSPECIFIED, @@ -194,7 +194,10 @@ public:  	{  		Mandatory<std::string>	type;  		Optional<S32>			width; +		Optional<S32>			max_length_chars; +		Optional<std::string>	text; +		Optional<std::string>	value;  		FormInput();  	}; @@ -268,6 +271,11 @@ struct LLNotificationTemplate;  // with smart pointers  typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr; + +struct LLNotificationVisibilityRule; + +typedef boost::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr; +  /**   * @class LLNotification   * @brief The object that expresses the details of a notification @@ -504,7 +512,7 @@ public:  	std::string getLabel() const;  	std::string getURL() const;  	S32 getURLOption() const; -	S32 getURLOpenExternally() const; +    S32 getURLOpenExternally() const;  	const LLNotificationFormPtr getForm(); @@ -576,6 +584,8 @@ public:  	bool hasUniquenessConstraints() const; +	bool matchesTag(const std::string& tag); +  	virtual ~LLNotification() {}  }; @@ -857,6 +867,10 @@ public:  	// OK to call more than once because it will reload  	bool loadTemplates();   +	// load visibility rules from file;  +	// OK to call more than once because it will reload +	bool loadVisibilityRules();   +	  	// Add a simple notification (from XUI)  	void addFromCallback(const LLSD& name); @@ -902,6 +916,8 @@ public:  	// test for existence  	bool templateExists(const std::string& name); +	typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList; +	  	void forceResponse(const LLNotification::Params& params, S32 option);  	void createDefaultChannels(); @@ -917,6 +933,8 @@ public:  	void setIgnoreAllNotifications(bool ignore);  	bool getIgnoreAllNotifications(); +	bool isVisibleByRules(LLNotificationPtr pNotification); +	  private:  	// we're a singleton, so we don't have a public constructor  	LLNotifications(); @@ -936,6 +954,8 @@ private:  	bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate);  	TemplateMap mTemplates; +	VisibilityRuleList mVisibilityRules; +  	std::string mFileName;  	LLNotificationMap mUniqueNotifications; @@ -973,17 +993,20 @@ public:  	{  		// upcast T to the base type to restrict T derivation from LLPostponedNotification  		LLPostponedNotification* thiz = new T(); -  		thiz->mParams = params; -		gCacheName->get(id, is_group, boost::bind( -				&LLPostponedNotification::onCachedNameReceived, thiz, _1, _2, -				_3, _4)); +		// Avoid header file dependency on llcachename.h +		lookupName(thiz, id, is_group);  	}  private: -	void onCachedNameReceived(const LLUUID& id, const std::string& first, -			const std::string& last, bool is_group); +	static void lookupName(LLPostponedNotification* thiz, const LLUUID& id, bool is_group); +	// only used for groups +	void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group); +	// only used for avatars +	void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); +	// used for both group and avatar names +	void finalizeName(const std::string& name);  	void cleanup()  	{ diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h index 6bc0d2aaff..eff572b553 100644 --- a/indra/llui/llnotificationtemplate.h +++ b/indra/llui/llnotificationtemplate.h @@ -74,11 +74,13 @@ struct LLNotificationTemplate  	struct UniquenessContext : public LLInitParam::Block<UniquenessContext>  	{ -		Mandatory<std::string>	key; +		Mandatory<std::string>	value;  		UniquenessContext() -		:	key("key") -		{} +		:	value("value") +		{ +			addSynonym(value, "key"); +		}  	}; @@ -88,7 +90,7 @@ struct LLNotificationTemplate  		// this idiom allows   		// <notification unique="true">  		// as well as -		// <notification> <unique> <context key=""/> </unique>... +		// <notification> <unique> <context></context> </unique>...  		Optional<bool>			dummy_val;  	public:  		Multiple<UniquenessContext>	contexts; @@ -156,6 +158,15 @@ struct LLNotificationTemplate  		{}  	}; +	struct Tag : public LLInitParam::Block<Tag> +	{ +		Mandatory<std::string>	value; + +		Tag() +		:	value("value") +		{} +	}; +  	struct Params : public LLInitParam::Block<Params>  	{  		Mandatory<std::string>			name; @@ -173,6 +184,7 @@ struct LLNotificationTemplate  		Optional<FormRef>				form_ref;  		Optional<ENotificationPriority,   			NotificationPriorityValues> priority; +		Multiple<Tag>		tags;  		Params() @@ -189,7 +201,8 @@ struct LLNotificationTemplate  			expire_option("expireOption", -1),  			url("url"),  			unique("unique"), -			form_ref("") +			form_ref(""), +			tags("tag")  		{}  	}; @@ -232,8 +245,8 @@ struct LLNotificationTemplate      // (used for things like progress indications, or repeating warnings      // like "the grid is going down in N minutes")      bool mUnique; -    // if we want to be unique only if a certain part of the payload is constant -    // specify the field names for the payload. The notification will only be +    // if we want to be unique only if a certain part of the payload or substitutions args +	// are constant specify the field names for the payload. The notification will only be      // combined if all of the fields named in the context are identical in the      // new and the old notification; otherwise, the notification will be      // duplicated. This is to support suppressing duplicate offers from the same @@ -276,6 +289,8 @@ struct LLNotificationTemplate  	// this is loaded as a name, but looked up to get the UUID upon template load.  	// If null, it wasn't specified.  	LLUUID mSoundEffect; +	// List of tags that rules can match against. +	std::list<std::string> mTags;  };  #endif //LL_LLNOTIFICATION_TEMPLATE_H diff --git a/indra/llui/llnotificationvisibilityrule.h b/indra/llui/llnotificationvisibilityrule.h new file mode 100644 index 0000000000..78bdec2a8f --- /dev/null +++ b/indra/llui/llnotificationvisibilityrule.h @@ -0,0 +1,104 @@ +/** +* @file llnotificationvisibility.h +* @brief Rules for  +* @author Monroe +* +* $LicenseInfo:firstyear=2010&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +*  +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +*  +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +*  +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +*  +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#ifndef LL_LLNOTIFICATION_VISIBILITY_RULE_H +#define LL_LLNOTIFICATION_VISIBILITY_RULE_H + +#include "llinitparam.h" +//#include "llnotifications.h" + + + +// This is the class of object read from the XML file (notification_visibility.xml,  +// from the appropriate local language directory). +struct LLNotificationVisibilityRule +{ +	struct Filter : public LLInitParam::Block<Filter> +	{ +		Optional<std::string>	type, +								tag, +								name; + +		Filter() +		:	type("type"), +			tag("tag"), +			name("name") +		{} +	}; + +	struct Respond : public LLInitParam::Block<Respond, Filter> +	{ +		Mandatory<std::string> response; + +		Respond() +		:	response("response") +		{} +	}; + +	struct Rule : public LLInitParam::Choice<Rule> +	{ +		Alternative<Filter>		show; +		Alternative<Filter>		hide; +		Alternative<Respond>	respond; + +		Rule() +		:	show("show"), +			hide("hide"), +			respond("respond") +		{} +	}; + +	struct Rules : public LLInitParam::Block<Rules> +	{ +		Multiple<Rule>	rules; + +		Rules() +		:	rules("") +		{} +	}; + +	LLNotificationVisibilityRule(const Rule& p); +	 +    // If true, this rule makes matching notifications visible.  Otherwise, it makes them invisible. +    bool mVisible; + +    // Which response to give when making a notification invisible.  An empty string means the notification should be cancelled instead of responded to. +	std::string mResponse; + +    // String to match against the notification's "type".  An empty string matches all notifications. +    std::string mType; +	 +    // String to match against the notification's tag(s).  An empty string matches all notifications. +	std::string mTag; + +    // String to match against the notification's name.  An empty string matches all notifications. +	std::string mName; +	 +}; + +#endif //LL_LLNOTIFICATION_VISIBILITY_RULE_H + diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index c8e56630f1..b2383106a8 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -194,6 +194,8 @@ void LLPanel::draw()  	// draw background  	if( mBgVisible )  	{ +		alpha = getCurrentTransparency(); +  		LLRect local_rect = getLocalRect();  		if (mBgOpaque )  		{ @@ -434,7 +436,7 @@ void LLPanel::initFromParams(const LLPanel::Params& p)      //and LLView::initFromParams will use them to set visible and enabled    	setVisible(p.visible);  	setEnabled(p.enabled); - +	setFocusRoot(p.focus_root);  	setSoundFlags(p.sound_flags);  	 // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible @@ -904,7 +906,7 @@ LLPanel *LLPanel::childGetVisiblePanelWithHelp()  		child = *it;  		// do we have a panel with a help topic?  		LLPanel *panel = dynamic_cast<LLPanel *>(child); -		if (panel && panel->getVisible() && !panel->getHelpTopic().empty()) +		if (panel && panel->isInVisibleChain() && !panel->getHelpTopic().empty())  		{  			return panel;  		} diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index aaa328754d..ead22686bc 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -50,7 +50,7 @@ LLProgressBar::Params::Params()  LLProgressBar::LLProgressBar(const LLProgressBar::Params& p)  -:	LLView(p), +:	LLUICtrl(p),  	mImageBar(p.image_bar),  	mImageFill(p.image_fill),  	mColorBackground(p.color_bg()), @@ -80,7 +80,7 @@ void LLProgressBar::draw()  	mImageFill->draw(progress_rect, bar_color);  } -void LLProgressBar::setPercent(const F32 percent) +void LLProgressBar::setValue(const LLSD& value)  { -	mPercentDone = llclamp(percent, 0.f, 100.f); +	mPercentDone = llclamp((F32)value.asReal(), 0.f, 100.f);  } diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h index 13297f7493..3f308e7496 100644 --- a/indra/llui/llprogressbar.h +++ b/indra/llui/llprogressbar.h @@ -27,14 +27,14 @@  #ifndef LL_LLPROGRESSBAR_H  #define LL_LLPROGRESSBAR_H -#include "llview.h" +#include "lluictrl.h"  #include "llframetimer.h"  class LLProgressBar -	: public LLView +	: public LLUICtrl  {  public: -	struct Params : public LLInitParam::Block<Params, LLView::Params> +	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>  	{  		Optional<LLUIImage*>	image_bar,  								image_fill; @@ -47,7 +47,7 @@ public:  	LLProgressBar(const Params&);  	virtual ~LLProgressBar(); -	void setPercent(const F32 percent); +	void setValue(const LLSD& value);  	/*virtual*/ void draw(); diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index cc348fdc63..6e9586369f 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -69,7 +69,7 @@ protected:  static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item");  LLRadioGroup::Params::Params() -:	has_border("draw_border"), +:	allow_deselect("allow_deselect"),  	items("item")   {  	addSynonym(items, "radio_item"); @@ -85,18 +85,8 @@ LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)  :	LLUICtrl(p),  	mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()),  	mSelectedIndex(-1), -	mHasBorder(p.has_border) -{	 -	if (mHasBorder) -	{ -		LLViewBorder::Params params; -		params.name("radio group border"); -		params.rect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0)); -		params.bevel_style(LLViewBorder::BEVEL_NONE); -		LLViewBorder * vb = LLUICtrlFactory::create<LLViewBorder> (params); -		addChild (vb); -	} -} +	mAllowDeselect(p.allow_deselect) +{}  void LLRadioGroup::initFromParams(const Params& p)  { @@ -184,7 +174,7 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)  BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)  { -	if (index < 0 || (S32)mRadioButtons.size() <= index ) +	if ((S32)mRadioButtons.size() <= index )  	{  		return FALSE;  	} @@ -202,13 +192,16 @@ BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)  	mSelectedIndex = index; -	LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; -	radio_item->setTabStop(true); -	radio_item->setValue( TRUE ); - -	if (hasFocus()) +	if (mSelectedIndex >= 0)  	{ -		mRadioButtons[mSelectedIndex]->focusFirstItem(FALSE, FALSE); +		LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; +		radio_item->setTabStop(true); +		radio_item->setValue( TRUE ); + +		if (hasFocus()) +		{ +			radio_item->focusFirstItem(FALSE, FALSE); +		}  	}  	if (!from_event) @@ -307,8 +300,15 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl)  		LLRadioCtrl* radio = *iter;  		if (radio == clicked_radio)  		{ -			// llinfos << "clicked button " << index << llendl; -			setSelectedIndex(index); +			if (index == mSelectedIndex && mAllowDeselect) +			{ +				// don't select anything +				setSelectedIndex(-1); +			} +			else +			{ +				setSelectedIndex(index); +			}  			// BUG: Calls click callback even if button didn't actually change  			onCommit(); diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index 0588900600..8bd5698538 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -49,7 +49,7 @@ public:  	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>  	{ -		Optional<bool>						has_border; +		Optional<bool>						allow_deselect;  		Multiple<ItemParams, AtLeast<1> >	items;  		Params();  	}; @@ -73,7 +73,6 @@ public:  	void setIndexEnabled(S32 index, BOOL enabled);  	// return the index value of the selected item  	S32 getSelectedIndex() const { return mSelectedIndex; } -	  	// set the index value programatically  	BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE); @@ -103,12 +102,13 @@ public:  	/*virtual*/ BOOL	operateOnAll(EOperation op);  private: -	const LLFontGL* mFont; -	S32 mSelectedIndex; +	const LLFontGL*		mFont; +	S32					mSelectedIndex; +  	typedef std::vector<class LLRadioCtrl*> button_list_t; -	button_list_t mRadioButtons; +	button_list_t		mRadioButtons; -	BOOL mHasBorder; +	bool				mAllowDeselect;	// user can click on an already selected option to deselect it  };  #endif diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 3146418a7d..380c477eb2 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -422,9 +422,10 @@ void LLScrollContainer::draw()  	// Draw background  	if( mIsOpaque )  	{ +		F32 alpha = getCurrentTransparency(); +  		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -		gGL.color4fv( mBackgroundColor.get().mV ); -		gl_rect_2d( mInnerRect ); +		gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha);  	}  	// Draw mScrolledViews and update scroll bars. diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index 2a4c1ca44c..696e4a2bb1 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -83,7 +83,14 @@ void LLScrollColumnHeader::draw()  			&& (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName);  	BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); -	setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent); +	if (draw_arrow) +	{ +		setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white); +	} +	else +	{ +		setImageOverlay(LLUUID::null); +	}  	// Draw children  	LLButton::draw(); diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 7df7c13dc0..b7848ec37c 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -322,6 +322,7 @@ LLScrollListCtrl::~LLScrollListCtrl()  	delete mSortCallback;  	std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); +	std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());  } @@ -1482,8 +1483,9 @@ void LLScrollListCtrl::draw()  	// Draw background  	if (mBackgroundVisible)  	{ +		F32 alpha = getCurrentTransparency();  		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -		gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() : mBgReadOnlyColor.get() ); +		gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha );  	}  	if (mColumnsDirty) @@ -2370,10 +2372,10 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar )  void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending)  { -	std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.find(name); +	column_map_t::iterator itor = mColumns.find(name);  	if (itor != mColumns.end())  	{ -		sortByColumnIndex((*itor).second.mIndex, ascending); +		sortByColumnIndex((*itor).second->mIndex, ascending);  	}  } @@ -2419,11 +2421,11 @@ void LLScrollListCtrl::dirtyColumns()  	// just in case someone indexes into it immediately  	mColumnsIndexed.resize(mColumns.size()); -	std::map<std::string, LLScrollListColumn>::iterator column_itor; +	column_map_t::iterator column_itor;  	for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)  	{ -		LLScrollListColumn *column = &column_itor->second; -		mColumnsIndexed[column_itor->second.mIndex] = column; +		LLScrollListColumn *column = column_itor->second; +		mColumnsIndexed[column_itor->second->mIndex] = column;  	}  } @@ -2581,8 +2583,8 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params  	if (mColumns.find(name) == mColumns.end())  	{  		// Add column -		mColumns[name] = LLScrollListColumn(column_params, this); -		LLScrollListColumn* new_column = &mColumns[name]; +		mColumns[name] = new LLScrollListColumn(column_params, this); +		LLScrollListColumn* new_column = mColumns[name];  		new_column->mIndex = mColumns.size()-1;  		// Add button @@ -2604,14 +2606,14 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params  			S32 top = mItemListRect.mTop;  			S32 left = mItemListRect.mLeft; -			for (std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.begin();  +			for (column_map_t::iterator itor = mColumns.begin();   				itor != mColumns.end();   				++itor)  			{ -				if (itor->second.mIndex < new_column->mIndex && -					itor->second.getWidth() > 0) +				if (itor->second->mIndex < new_column->mIndex && +					itor->second->getWidth() > 0)  				{ -					left += itor->second.getWidth() + mColumnPadding; +					left += itor->second->getWidth() + mColumnPadding;  				}  			} @@ -2667,8 +2669,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata)  	if (column->mSortingColumn != column->mName  		&& parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())  	{ -		LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; -		column_index = info_redir.mIndex; +		LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn]; +		column_index = info_redir->mIndex;  	}  	// if this column is the primary sort key, reverse the direction @@ -2701,16 +2703,17 @@ BOOL LLScrollListCtrl::hasSortOrder() const  void LLScrollListCtrl::clearColumns()  { -	std::map<std::string, LLScrollListColumn>::iterator itor; +	column_map_t::iterator itor;  	for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)  	{ -		LLScrollColumnHeader *header = itor->second.mHeader; +		LLScrollColumnHeader *header = itor->second->mHeader;  		if (header)  		{  			removeChild(header);  			delete header;  		}  	} +	std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());  	mColumns.clear();  	mSortColumns.clear();  	mTotalStaticColumnWidth = 0; @@ -2744,7 +2747,7 @@ LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name)  	column_map_t::iterator column_itor = mColumns.find(name);  	if (column_itor != mColumns.end())   	{ -		return &column_itor->second; +		return column_itor->second;  	}  	return NULL;  } @@ -2805,7 +2808,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS  				new_column.width.pixel_width = cell_p.width;  			}  			addColumn(new_column); -			columnp = &mColumns[column]; +			columnp = mColumns[column];  			new_item->setNumColumns(mColumns.size());  		} @@ -2842,7 +2845,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS  		LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value));  		if (cell)  		{ -			LLScrollListColumn* columnp = &(mColumns.begin()->second); +			LLScrollListColumn* columnp = mColumns.begin()->second;  			new_item->setColumn(0, cell);  			if (columnp->mHeader  @@ -2857,10 +2860,10 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS  	// add dummy cells for missing columns  	for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it)  	{ -		S32 column_idx = column_it->second.mIndex; +		S32 column_idx = column_it->second->mIndex;  		if (new_item->getColumn(column_idx) == NULL)  		{ -			LLScrollListColumn* column_ptr = &column_it->second; +			LLScrollListColumn* column_ptr = column_it->second;  			LLScrollListCell::Params cell_p;  			cell_p.width = column_ptr->getWidth(); diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 8a2f893ba2..09ab89960d 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -491,7 +491,7 @@ private:  	mutable bool	mSorted; -	typedef std::map<std::string, LLScrollListColumn> column_map_t; +	typedef std::map<std::string, LLScrollListColumn*> column_map_t;  	column_map_t mColumns;  	BOOL			mDirty; diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp index f97f80ab6c..9ad13054cb 100644 --- a/indra/llui/llsdparam.cpp +++ b/indra/llui/llsdparam.cpp @@ -45,6 +45,7 @@ LLParamSDParser::LLParamSDParser()  	if (sReadFuncs.empty())  	{ +		registerParserFuncs<LLInitParam::NoParamValue>(readNoValue, &LLParamSDParser::writeNoValue);  		registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>);  		registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param);  		registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>); @@ -71,6 +72,18 @@ bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const voi  	return true;  } +bool LLParamSDParser::writeNoValue(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack) +{ +	LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); +	if (!sdparser.mWriteRootSD) return false; + +	LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack); +	if (!sd_to_write) return false; + +	return true; +} + +  void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent)  {  	mCurReadSD = NULL; @@ -87,6 +100,8 @@ void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block)  	block.serializeBlock(*this);  } +const LLSD NO_VALUE_MARKER; +  void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block)  {  	if (sd.isMap()) @@ -110,6 +125,11 @@ void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block  			readSDValues(*it, block);  		}  	} +	else if (sd.isUndefined()) +	{ +		mCurReadSD = &NO_VALUE_MARKER; +		block.submitValue(mNameStack, *this); +	}  	else  	{  		mCurReadSD = &sd; @@ -206,6 +226,13 @@ LLSD* LLParamSDParser::getSDWriteNode(const parser_t::name_stack_t& name_stack)  	return sd_to_write;  } +bool LLParamSDParser::readNoValue(Parser& parser, void* val_ptr) +{ +	LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); +	return self.mCurReadSD == &NO_VALUE_MARKER; +} + +  bool LLParamSDParser::readS32(Parser& parser, void* val_ptr)  {  	LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h index 97e8b58e49..69dab2b411 100644 --- a/indra/llui/llsdparam.h +++ b/indra/llui/llsdparam.h @@ -63,7 +63,9 @@ private:  	LLSD* getSDWriteNode(const parser_t::name_stack_t& name_stack);  	static bool writeU32Param(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack); +	static bool writeNoValue(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack); +	static bool readNoValue(Parser& parser, void* val_ptr);  	static bool readS32(Parser& parser, void* val_ptr);  	static bool readU32(Parser& parser, void* val_ptr);  	static bool readF32(Parser& parser, void* val_ptr); diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 9decfa0b25..6b4e9cf923 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -119,7 +119,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)  	{  		params.font(p.font);  	} -	params.max_length_bytes(MAX_STRING_LENGTH); +	params.max_length.bytes(MAX_STRING_LENGTH);  	params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));  	if( mPrecision>0 )//should accept float numbers diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 5e09cee78b..28a064e6b6 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -40,7 +40,8 @@ LLStyle::Params::Params()  	selected_color("selected_color", LLColor4::black),  	font("font", LLFontGL::getFontMonospace()),  	image("image"), -	link_href("href") +	link_href("href"), +	is_link("is_link")  {} @@ -51,6 +52,7 @@ LLStyle::LLStyle(const LLStyle::Params& p)  	mSelectedColor(p.selected_color),  	mFont(p.font()),  	mLink(p.link_href), +	mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()),  	mDropShadow(p.drop_shadow),  	mImagep(p.image())  {} @@ -73,7 +75,7 @@ void LLStyle::setLinkHREF(const std::string& href)  BOOL LLStyle::isLink() const  { -	return mLink.size(); +	return mIsLink;  }  BOOL LLStyle::isVisible() const diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 66cd639936..322edc343c 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -46,6 +46,7 @@ public:  		Optional<const LLFontGL*>		font;  		Optional<LLUIImage*>			image;  		Optional<std::string>			link_href; +		Optional<bool>					is_link;  		Params();  	};  	LLStyle(const Params& p = Params()); @@ -106,6 +107,7 @@ private:  	std::string			mFontName;  	const LLFontGL*		mFont;  	std::string			mLink; +	bool				mIsLink;  	LLUIImagePtr		mImagep;  }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 574b24cf13..49537ef78f 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2009&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2009-2010, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -475,8 +475,8 @@ void LLTextBase::drawCursor()  			{  				LLColor4 text_color;  				const LLFontGL* fontp; -					text_color = segmentp->getColor(); -					fontp = segmentp->getStyle()->getFont(); +				text_color = segmentp->getColor(); +				fontp = segmentp->getStyle()->getFont();  				fontp->render(text, mCursorPos, cursor_rect,   					LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha),  					LLFontGL::LEFT, mVAlign, @@ -1005,6 +1005,7 @@ void LLTextBase::draw()  	if (mBGVisible)  	{ +		F32 alpha = getCurrentTransparency();  		// clip background rect against extents, if we support scrolling  		LLRect bg_rect = mVisibleTextRect;  		if (mScroller) @@ -1016,7 +1017,7 @@ void LLTextBase::draw()  							: hasFocus()   								? mFocusBgColor.get()   								: mWriteableBgColor.get(); -		gl_rect_2d(doc_rect, bg_color, TRUE); +		gl_rect_2d(doc_rect, bg_color % alpha, TRUE);  	}  	// draw document view @@ -1591,7 +1592,10 @@ void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params&  	// appendText modifies mCursorPos...  	appendText(text, false, input_params);  	// ...so move cursor to top after appending text -	startOfDoc(); +	if (!mTrackEnd) +	{ +		startOfDoc(); +	}  	onValueChange(0, getLength());  } @@ -1602,28 +1606,39 @@ std::string LLTextBase::getText() const  	return getViewModel()->getValue().asString();  } +// IDEVO - icons can be UI image names or UUID sent from +// server with avatar display name +static LLUIImagePtr image_from_icon_name(const std::string& icon_name) +{ +	if (LLUUID::validate(icon_name)) +	{ +		return LLUI::getUIImageByID( LLUUID(icon_name) ); +	} +	else +	{ +		return LLUI::getUIImage(icon_name); +	} +} +  void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)  {  	LLStyle::Params style_params(input_params);  	style_params.fillFrom(getDefaultStyleParams());  	S32 part = (S32)LLTextParser::WHOLE; -	if(mParseHTML) +	if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358).  	{  		S32 start=0,end=0;  		LLUrlMatch match;  		std::string text = new_text;  		while ( LLUrlRegistry::instance().findUrl(text, match, -		        boost::bind(&LLTextBase::replaceUrlLabel, this, _1, _2)) ) +		        boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) )  		{  			start = match.getStart();  			end = match.getEnd()+1; -			LLStyle::Params link_params = style_params; -			link_params.color = match.getColor(); -			link_params.readonly_color =  match.getColor(); -			link_params.font.style("UNDERLINE"); -			link_params.link_href = match.getUrl(); +			LLStyle::Params link_params(style_params); +			link_params.overwriteFrom(match.getStyle());  			// output the text before the Url  			if (start > 0) @@ -1644,26 +1659,20 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para  			// inserts an avatar icon preceding the Url if appropriate  			LLTextUtil::processUrlMatch(&match,this); -			// output the styled Url (unless we've been asked to suppress hyperlinking) -			if (match.isLinkDisabled()) +			// output the styled Url +			appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); +			 +			// set the tooltip for the Url label +			if (! match.getTooltip().empty())  			{ -				appendAndHighlightText(match.getLabel(), part, style_params); +				segment_set_t::iterator it = getSegIterContaining(getLength()-1); +				if (it != mSegments.end()) +					{ +						LLTextSegmentPtr segment = *it; +						segment->setToolTip(match.getTooltip()); +					}  			} -			else -			{ -				appendAndHighlightText(match.getLabel(), 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()) -						{ -							LLTextSegmentPtr segment = *it; -							segment->setToolTip(match.getTooltip()); -						} -				} -			}  			// move on to the rest of the text after the Url  			if (end < (S32)text.length())   			{ @@ -1848,8 +1857,9 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlig  } -void LLTextBase::replaceUrlLabel(const std::string &url, -								   const std::string &label) +void LLTextBase::replaceUrl(const std::string &url, +							const std::string &label, +							const std::string &icon)  {  	// get the full (wide) text for the editor so we can change it  	LLWString text = getWText(); @@ -1870,7 +1880,7 @@ void LLTextBase::replaceUrlLabel(const std::string &url,  		seg->setEnd(seg_start + seg_length);  		// if we find a link with our Url, then replace the label -		if (style->isLink() && style->getLinkHREF() == url) +		if (style->getLinkHREF() == url)  		{  			S32 start = seg->getStart();  			S32 end = seg->getEnd(); @@ -1879,6 +1889,21 @@ void LLTextBase::replaceUrlLabel(const std::string &url,  			modified = true;  		} +		// Icon might be updated when more avatar or group info +		// becomes available +		if (style->isImage() && style->getLinkHREF() == url) +		{ +			LLUIImagePtr image = image_from_icon_name( icon ); +			if (image) +			{ +				LLStyle::Params icon_params; +				icon_params.image = image; +				LLStyleConstSP new_style(new LLStyle(icon_params)); +				seg->setStyle(new_style); +				modified = true; +			} +		} +  		// work out the character offset for the next segment  		seg_start = seg->getEnd();  	} @@ -1976,8 +2001,8 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,  		else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1)	  		{  			// segment wraps to next line, so just set doc pos to the end of the line - 			// segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) - 			pos = llmin(getLength(), line_iter->mDocIndexEnd); +			// segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd) +			pos = llmin(getLength(), line_iter->mDocIndexEnd);  			break;  		}  		start_x += text_width; @@ -2193,19 +2218,39 @@ bool LLTextBase::scrolledToEnd()  	return mScroller->isAtBottom();  } -  bool LLTextBase::setCursor(S32 row, S32 column)  { -	if (0 <= row && row < (S32)mLineInfoList.size()) +	if (row < 0 || column < 0) return false; + +	S32 n_lines = mLineInfoList.size(); +	for (S32 line = row; line < n_lines; ++line)  	{ -		S32 doc_pos = mLineInfoList[row].mDocIndexStart; -		column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1); -		doc_pos += column; -		updateCursorXPos(); +		const line_info& li = mLineInfoList[line]; + +		if (li.mLineNum < row) +		{ +			continue; +		} +		else if (li.mLineNum > row) +		{ +			break; // invalid column specified +		} +		// Found the given row. +		S32 line_length = li.mDocIndexEnd - li.mDocIndexStart;; +		if (column >= line_length) +		{ +			column -= line_length; +			continue; +		} + +		// Found the given column. +		updateCursorXPos(); +		S32 doc_pos = li.mDocIndexStart + column;  		return setCursorPos(doc_pos);  	} -	return false; + +	return false; // invalid row or column specified  } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 1fa449a182..aafcf8ceb0 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -493,7 +493,11 @@ protected:  	// misc  	void							updateRects();  	void							needsScroll() { mScrollNeeded = TRUE; } -	void							replaceUrlLabel(const std::string &url, const std::string &label); + +	struct URLLabelCallback; +	// Replace a URL with a new icon and label, for example, when +	// avatar names are looked up. +	void replaceUrl(const std::string &url, const std::string &label, const std::string& icon);  	void							appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params());  	void							appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 94bf716e7d..5a46c7c98e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -277,6 +277,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :  		mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;  		updateRects();  	} +	 +	mParseOnTheFly = TRUE;  }  void LLTextEditor::initFromParams( const LLTextEditor::Params& p) @@ -324,8 +326,10 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param  	blockUndo();  	deselect(); - +	 +	mParseOnTheFly = FALSE;  	LLTextBase::setText(utf8str, input_params); +	mParseOnTheFly = TRUE;  	resetDirty();  } @@ -1367,6 +1371,7 @@ void LLTextEditor::pastePrimary()  // paste from primary (itsprimary==true) or clipboard (itsprimary==false)  void LLTextEditor::pasteHelper(bool is_primary)  { +	mParseOnTheFly = FALSE;  	bool can_paste_it;  	if (is_primary)  	{ @@ -1450,6 +1455,7 @@ void LLTextEditor::pasteHelper(bool is_primary)  	deselect();  	onKeyStroke(); +	mParseOnTheFly = TRUE;  } @@ -2385,7 +2391,7 @@ void LLTextEditor::loadKeywords(const std::string& filename,  void LLTextEditor::updateSegments()  { -	if (mReflowIndex < S32_MAX && mKeywords.isLoaded()) +	if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly)  	{  		LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);  		// HACK:  No non-ascii keywords for now diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 58ecefdccb..9e4b95003b 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -315,6 +315,7 @@ private:  	BOOL			mAllowEmbeddedItems;  	bool			mShowContextMenu; +	bool			mParseOnTheFly;  	LLUUID			mSourceID; diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp index 0eb2dc1387..d29260750f 100644 --- a/indra/llui/lltoggleablemenu.cpp +++ b/indra/llui/lltoggleablemenu.cpp @@ -35,10 +35,22 @@ static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu");  LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p)  :	LLMenuGL(p),  	mButtonRect(), +	mVisibilityChangeSignal(NULL),  	mClosedByButtonClick(false)  {  } +LLToggleableMenu::~LLToggleableMenu() +{ +	delete mVisibilityChangeSignal; +} + +boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const commit_signal_t::slot_type& cb) +{ +	if (!mVisibilityChangeSignal) mVisibilityChangeSignal = new commit_signal_t(); +	return mVisibilityChangeSignal->connect(cb); +} +  // virtual  void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn)  { @@ -49,6 +61,12 @@ void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn)  	{  		mClosedByButtonClick = true;  	} + +	if (mVisibilityChangeSignal) +	{ +		(*mVisibilityChangeSignal)(this, +				LLSD().with("visibility", curVisibilityIn).with("closed_by_button_click", mClosedByButtonClick)); +	}  }  void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view) diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h index f036cdfffb..2094bd776f 100644 --- a/indra/llui/lltoggleablemenu.h +++ b/indra/llui/lltoggleablemenu.h @@ -41,6 +41,10 @@ protected:  	LLToggleableMenu(const Params&);  	friend class LLUICtrlFactory;  public: +	~LLToggleableMenu(); + +	boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb ); +  	virtual void handleVisibilityChange (BOOL curVisibilityIn);  	const LLRect& getButtonRect() const { return mButtonRect; } @@ -57,6 +61,7 @@ public:  protected:  	bool mClosedByButtonClick;  	LLRect mButtonRect; +	commit_signal_t*	mVisibilityChangeSignal;  };  #endif // LL_LLTOGGLEABLEMENU_H diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index ff9af21e54..c300fe55d9 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -950,7 +950,7 @@ void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor  }  // Draw gray and white checkerboard with black border -void gl_rect_2d_checkerboard(const LLRect& rect) +void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha)  {  	// Initialize the first time this is called.  	const S32 PIXELS = 32; @@ -971,11 +971,11 @@ void gl_rect_2d_checkerboard(const LLRect& rect)  	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);  	// ...white squares -	gGL.color3f( 1.f, 1.f, 1.f ); +	gGL.color4f( 1.f, 1.f, 1.f, alpha );  	gl_rect_2d(rect);  	// ...gray squares -	gGL.color3f( .7f, .7f, .7f ); +	gGL.color4f( .7f, .7f, .7f, alpha );  	gGL.flush();  	glPolygonStipple( checkerboard ); @@ -1596,23 +1596,25 @@ void LLUI::initClass(const settings_map_t& settings,  	sWindow = NULL; // set later in startup  	LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow"); +	LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar(); +  	// Callbacks for associating controls with floater visibilty: -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2)); -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2)); -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2)); -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2)); +	reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2)); +	reg.add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2)); +	reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2)); +	reg.add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2));  	// Button initialization callback for toggle buttons -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); +	reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2));  	// Button initialization callback for toggle buttons on dockale floaters -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); +	reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));  	// Display the help topic for the current context -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); +	reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2));  	// Currently unused, but kept for reference: -	LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); +	reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2));  	// Used by menus along with Floater.Toggle to display visibility as a checkmark  	LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::floaterInstanceVisible, _2)); @@ -1620,7 +1622,10 @@ void LLUI::initClass(const settings_map_t& settings,  void LLUI::cleanupClass()  { -	sImageProvider->cleanUp(); +	if(sImageProvider) +	{ +		sImageProvider->cleanUp(); +	}  }  void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup,  const clear_popups_t& clear_popups) diff --git a/indra/llui/llui.h b/indra/llui/llui.h index fc545c85d5..62d10df8b2 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -79,7 +79,7 @@ void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LL  void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, BOOL filled = TRUE );  void gl_rect_2d(const LLRect& rect, BOOL filled = TRUE );  void gl_rect_2d(const LLRect& rect, const LLColor4& color, BOOL filled = TRUE ); -void gl_rect_2d_checkerboard(const LLRect& rect); +void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f);  void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines); diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 0641f6d175..9455d09cc0 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -215,6 +215,12 @@ bool LLUIColorTable::loadFromSettings()  		result |= loadFromFilename(current_filename, mLoadedColors);  	} +	current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml"); +	if(current_filename != default_filename) +	{ +		result |= loadFromFilename(current_filename, mLoadedColors); +	} +  	std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");  	loadFromFilename(user_filename, mUserSetColors); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 3ac3bf8c41..0a06b5e74f 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -36,6 +36,9 @@  static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl"); +F32 LLUICtrl::sActiveControlTransparency = 1.0f; +F32 LLUICtrl::sInactiveControlTransparency = 1.0f; +  // Compiler optimization, generate extern template  template class LLUICtrl* LLView::getChild<class LLUICtrl>(  	const std::string& name, BOOL recurse) const; @@ -110,7 +113,8 @@ LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)  	mMouseUpSignal(NULL),  	mRightMouseDownSignal(NULL),  	mRightMouseUpSignal(NULL), -	mDoubleClickSignal(NULL) +	mDoubleClickSignal(NULL), +	mTransparencyType(TT_DEFAULT)  {  	mUICtrlHandle.bind(this);  } @@ -823,7 +827,7 @@ LLUICtrl* LLUICtrl::findRootMostFocusRoot()  {  	LLUICtrl* focus_root = NULL;  	LLUICtrl* next_view = this; -	while(next_view) +	while(next_view && next_view->hasTabStop())  	{  		if (next_view->isFocusRoot())  		{ @@ -923,6 +927,37 @@ BOOL LLUICtrl::getTentative() const  void LLUICtrl::setColor(const LLColor4& color)							  { } +F32 LLUICtrl::getCurrentTransparency() +{ +	F32 alpha = 0; + +	switch(mTransparencyType) +	{ +	case TT_DEFAULT: +		alpha = getDrawContext().mAlpha; +		break; + +	case TT_ACTIVE: +		alpha = sActiveControlTransparency; +		break; + +	case TT_INACTIVE: +		alpha = sInactiveControlTransparency; +		break; + +	case TT_FADING: +		alpha = sInactiveControlTransparency / 2; +		break; +	} + +	return alpha; +} + +void LLUICtrl::setTransparencyType(ETypeTransparency type) +{ +	mTransparencyType = type; +} +  boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb )   {   	if (!mCommitSignal) mCommitSignal = new commit_signal_t(); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 76dfdf754c..b37e9f6b1b 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -120,6 +120,13 @@ public:  		Params();  	}; +	enum ETypeTransparency +	{ +		TT_DEFAULT, +		TT_ACTIVE,		// focused floater +		TT_INACTIVE,	// other floaters +		TT_FADING,		// fading toast +	};  	/*virtual*/ ~LLUICtrl();  	void initFromParams(const Params& p); @@ -202,6 +209,11 @@ public:  	virtual void	setColor(const LLColor4& color); +	F32 			getCurrentTransparency(); + +	void				setTransparencyType(ETypeTransparency type); +	ETypeTransparency	getTransparencyType() const {return mTransparencyType;} +  	BOOL	focusNextItem(BOOL text_entry_only);  	BOOL	focusPrevItem(BOOL text_entry_only);  	BOOL 	focusFirstItem(BOOL prefer_text_fields = FALSE, BOOL focus_flash = TRUE ); @@ -283,6 +295,10 @@ protected:  	boost::signals2::connection mMakeVisibleControlConnection;  	LLControlVariable* mMakeInvisibleControlVariable;  	boost::signals2::connection mMakeInvisibleControlConnection; + +	static F32 sActiveControlTransparency; +	static F32 sInactiveControlTransparency; +  private:  	BOOL			mTabStop; @@ -290,6 +306,8 @@ private:  	BOOL			mTentative;  	LLRootHandle<LLUICtrl> mUICtrlHandle; +	ETypeTransparency mTransparencyType; +  	class DefaultTabGroupFirstSorter;  }; diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h index eff2467bf0..cb40c85582 100644 --- a/indra/llui/lluistring.h +++ b/indra/llui/lluistring.h @@ -58,9 +58,10 @@ class LLUIString  public:  	// These methods all perform appropriate argument substitution  	// and modify mOrig where appropriate -        LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {} +	LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {}  	LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);  	LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); } +	~LLUIString() { delete mArgs; }  	void assign(const std::string& instring);  	LLUIString& operator=(const std::string& s) { assign(s); return *this; } @@ -81,14 +82,14 @@ public:  	void clear();  	void clearArgs() { if (mArgs) mArgs->clear(); } -	 +  	// These utility functions are included for text editing.  	// They do not affect mOrig and do not perform argument substitution  	void truncate(S32 maxchars);  	void erase(S32 charidx, S32 len);  	void insert(S32 charidx, const LLWString& wchars);  	void replace(S32 charidx, llwchar wc); -	 +  private:  	// something changed, requiring reformatting of strings  	void dirty(); @@ -100,7 +101,7 @@ private:  	void updateResult() const;  	void updateWResult() const;  	LLStringUtil::format_map_t& getArgs(); -	 +  	std::string mOrig;  	mutable std::string mResult;  	mutable LLWString mWResult; // for displaying diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 5680ab8bd4..9db1feafd1 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -27,22 +27,25 @@  #include "linden_common.h"  #include "llurlentry.h" +#include "lluictrl.h"  #include "lluri.h"  #include "llurlmatch.h"  #include "llurlregistry.h" +#include "llavatarnamecache.h"  #include "llcachename.h"  #include "lltrans.h"  #include "lluicolortable.h" +#include "message.h"  #define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" +// Utility functions +std::string localize_slapp_label(const std::string& url, const std::string& full_name); -LLUrlEntryBase::LLUrlEntryBase() : -	mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")), -	mDisabledLink(false) -{ -} + +LLUrlEntryBase::LLUrlEntryBase() +{}  LLUrlEntryBase::~LLUrlEntryBase()  { @@ -53,6 +56,22 @@ std::string LLUrlEntryBase::getUrl(const std::string &string) const  	return escapeUrl(string);  } +//virtual +std::string LLUrlEntryBase::getIcon(const std::string &url) +{ +	return mIcon; +} + +LLStyle::Params LLUrlEntryBase::getStyle() const +{ +	LLStyle::Params style_params; +	style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +	style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +	style_params.font.style = "UNDERLINE"; +	return style_params; +} + +  std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const  {  	// return the id from a SLURL in the format /app/{cmd}/{id}/about @@ -130,22 +149,35 @@ void LLUrlEntryBase::addObserver(const std::string &id,  		mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer));  	}  } -  -void LLUrlEntryBase::callObservers(const std::string &id, const std::string &label) + +// *NOTE: See also LLUrlEntryAgent::callObservers() +void LLUrlEntryBase::callObservers(const std::string &id, +								   const std::string &label, +								   const std::string &icon)  {  	// notify all callbacks waiting on the given uuid -	std::multimap<std::string, LLUrlEntryObserver>::iterator it; -	for (it = mObservers.find(id); it != mObservers.end();) +	typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it; +	std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id); +	for (observer_it it = matching_range.first; it != matching_range.second;)  	{  		// call the callback - give it the new label  		LLUrlEntryObserver &observer = it->second; -		(*observer.signal)(it->second.url, label); +		(*observer.signal)(it->second.url, label, icon);  		// then remove the signal - we only need to call it once  		delete observer.signal;  		mObservers.erase(it++);  	}  } +/// is this a match for a URL that should not be hyperlinked? +bool LLUrlEntryBase::isLinkDisabled() const +{ +	// this allows us to have a global setting to turn off text hyperlink highlighting/action +	bool globally_disabled = LLUI::sSettingGroups["config"]->getBOOL("DisableTextHyperlinkActions"); + +	return globally_disabled; +} +  static std::string getStringAfterToken(const std::string str, const std::string token)  {  	size_t pos = str.find(token); @@ -188,7 +220,13 @@ LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()  std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)  { -	return getLabelFromWikiLink(url); +	std::string label = getLabelFromWikiLink(url); +	return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); +} + +std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const +{ +	return getUrl(string);  }  std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const @@ -308,16 +346,35 @@ LLUrlEntryAgent::LLUrlEntryAgent()  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_agent.xml";  	mIcon = "Generic_Person"; -	mColor = LLUIColorTable::instance().getColor("AgentLinkColor");  } -void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id, -										  const std::string& first, -										  const std::string& last, -										  BOOL is_group) +// virtual +void LLUrlEntryAgent::callObservers(const std::string &id, +								    const std::string &label, +								    const std::string &icon) +{ +	// notify all callbacks waiting on the given uuid +	typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it; +	std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id); +	for (observer_it it = matching_range.first; it != matching_range.second;) +	{ +		// call the callback - give it the new label +		LLUrlEntryObserver &observer = it->second; +		std::string final_label = localize_slapp_label(observer.url, label); +		(*observer.signal)(observer.url, final_label, icon); +		// then remove the signal - we only need to call it once +		delete observer.signal; +		mObservers.erase(it++); +	} +} + +void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, +										const LLAvatarName& av_name)  { +	std::string label = av_name.getCompleteName(); +  	// received the agent name from the server - tell our observers -	callObservers(id.asString(), first + " " + last); +	callObservers(id.asString(), label, mIcon);  }  LLUUID	LLUrlEntryAgent::getID(const std::string &string) const @@ -330,6 +387,10 @@ std::string LLUrlEntryAgent::getTooltip(const std::string &string) const  	// return a tooltip corresponding to the URL type instead of the generic one  	std::string url = getUrl(string); +	if (LLStringUtil::endsWith(url, "/inspect")) +	{ +		return LLTrans::getString("TooltipAgentInspect"); +	}  	if (LLStringUtil::endsWith(url, "/mute"))  	{  		return LLTrans::getString("TooltipAgentMute"); @@ -379,50 +440,182 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa  	}  	LLUUID agent_id(agent_id_string); -	std::string full_name;  	if (agent_id.isNull())  	{  		return LLTrans::getString("AvatarNameNobody");  	} -	else if (gCacheName->getFullName(agent_id, full_name)) + +	LLAvatarName av_name; +	if (LLAvatarNameCache::get(agent_id, &av_name))  	{ -		// customize label string based on agent SLapp suffix -		if (LLStringUtil::endsWith(url, "/mute")) -		{ -			return LLTrans::getString("SLappAgentMute") + " " + full_name; -		} -		if (LLStringUtil::endsWith(url, "/unmute")) -		{ -			return LLTrans::getString("SLappAgentUnmute") + " " + full_name; -		} -		if (LLStringUtil::endsWith(url, "/im")) -		{ -			return LLTrans::getString("SLappAgentIM") + " " + full_name; -		} -		if (LLStringUtil::endsWith(url, "/pay")) -		{ -			return LLTrans::getString("SLappAgentPay") + " " + full_name; -		} -		if (LLStringUtil::endsWith(url, "/offerteleport")) -		{ -			return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name; -		} -		if (LLStringUtil::endsWith(url, "/requestfriend")) -		{ -			return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name; -		} -		return full_name; +		std::string label = av_name.getCompleteName(); + +		// handle suffixes like /mute or /offerteleport +		label = localize_slapp_label(url, label); +		return label; +	} +	else +	{ +		LLAvatarNameCache::get(agent_id, +			boost::bind(&LLUrlEntryAgent::onAvatarNameCache, +				this, _1, _2)); +		addObserver(agent_id_string, url, cb); +		return LLTrans::getString("LoadingData"); +	} +} + +LLStyle::Params LLUrlEntryAgent::getStyle() const +{ +	LLStyle::Params style_params = LLUrlEntryBase::getStyle(); +	style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +	style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +	return style_params; +} + +std::string localize_slapp_label(const std::string& url, const std::string& full_name) +{ +	// customize label string based on agent SLapp suffix +	if (LLStringUtil::endsWith(url, "/mute")) +	{ +		return LLTrans::getString("SLappAgentMute") + " " + full_name; +	} +	if (LLStringUtil::endsWith(url, "/unmute")) +	{ +		return LLTrans::getString("SLappAgentUnmute") + " " + full_name; +	} +	if (LLStringUtil::endsWith(url, "/im")) +	{ +		return LLTrans::getString("SLappAgentIM") + " " + full_name; +	} +	if (LLStringUtil::endsWith(url, "/pay")) +	{ +		return LLTrans::getString("SLappAgentPay") + " " + full_name; +	} +	if (LLStringUtil::endsWith(url, "/offerteleport")) +	{ +		return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name; +	} +	if (LLStringUtil::endsWith(url, "/requestfriend")) +	{ +		return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name; +	} +	return full_name; +} + + +std::string LLUrlEntryAgent::getIcon(const std::string &url) +{ +	// *NOTE: Could look up a badge here by calling getIDStringFromUrl() +	// and looking up the badge for the agent. +	return mIcon; +} + +// +// LLUrlEntryAgentName describes a Second Life agent name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// +LLUrlEntryAgentName::LLUrlEntryAgentName() +{} + +void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, +										const LLAvatarName& av_name) +{ +	std::string label = getName(av_name); +	// received the agent name from the server - tell our observers +	callObservers(id.asString(), label, mIcon); +} + +std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ +	if (!gCacheName) +	{ +		// probably at the login screen, use short string for layout +		return LLTrans::getString("LoadingData"); +	} + +	std::string agent_id_string = getIDStringFromUrl(url); +	if (agent_id_string.empty()) +	{ +		// something went wrong, just give raw url +		return unescapeUrl(url); +	} + +	LLUUID agent_id(agent_id_string); +	if (agent_id.isNull()) +	{ +		return LLTrans::getString("AvatarNameNobody"); +	} + +	LLAvatarName av_name; +	if (LLAvatarNameCache::get(agent_id, &av_name)) +	{ +		return getName(av_name);  	}  	else  	{ -		gCacheName->get(agent_id, FALSE, -			boost::bind(&LLUrlEntryAgent::onAgentNameReceived, -				this, _1, _2, _3, _4)); +		LLAvatarNameCache::get(agent_id, +			boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache, +				this, _1, _2));  		addObserver(agent_id_string, url, cb);  		return LLTrans::getString("LoadingData");  	}  } +LLStyle::Params LLUrlEntryAgentName::getStyle() const +{ +	// don't override default colors +	return LLStyle::Params().is_link(false); +} + +// +// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// +LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() +{ +	mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename", +							boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name) +{ +	return avatar_name.getCompleteName(); +} + +// +// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// +LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() +{ +	mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname", +							boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) +{ +	return avatar_name.mDisplayName; +} + +// +// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// +LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() +{ +	mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username", +							boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) +{ +	return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername; +} +  //  // LLUrlEntryGroup Describes a Second Life group Url, e.g.,  // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about @@ -436,18 +629,16 @@ LLUrlEntryGroup::LLUrlEntryGroup()  	mMenuName = "menu_url_group.xml";  	mIcon = "Generic_Group";  	mTooltip = LLTrans::getString("TooltipGroupUrl"); -	mColor = LLUIColorTable::instance().getColor("GroupLinkColor");  }  void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id, -										  const std::string& first, -										  const std::string& last, -										  BOOL is_group) +										  const std::string& name, +										  bool is_group)  {  	// received the group name from the server - tell our observers -	callObservers(id.asString(), first); +	callObservers(id.asString(), name, mIcon);  }  LLUUID	LLUrlEntryGroup::getID(const std::string &string) const @@ -483,14 +674,23 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa  	}  	else  	{ -		gCacheName->get(group_id, TRUE, +		gCacheName->getGroup(group_id,  			boost::bind(&LLUrlEntryGroup::onGroupNameReceived, -				this, _1, _2, _3, _4)); +				this, _1, _2, _3));  		addObserver(group_id_string, url, cb);  		return LLTrans::getString("LoadingData");  	}  } +LLStyle::Params LLUrlEntryGroup::getStyle() const +{ +	LLStyle::Params style_params = LLUrlEntryBase::getStyle(); +	style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +	style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +	return style_params; +} + +  //  // LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,  // secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select @@ -541,6 +741,13 @@ std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const  	return LLUrlEntryBase::getLocation(url);  } +// LLUrlEntryParcel statics. +LLUUID	LLUrlEntryParcel::sAgentID(LLUUID::null); +LLUUID	LLUrlEntryParcel::sSessionID(LLUUID::null); +LLHost	LLUrlEntryParcel::sRegionHost(LLHost::invalid); +bool	LLUrlEntryParcel::sDisconnected(false); +std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers; +  ///  /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,  /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about @@ -552,13 +759,88 @@ LLUrlEntryParcel::LLUrlEntryParcel()  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_parcel.xml";  	mTooltip = LLTrans::getString("TooltipParcelUrl"); + +	sParcelInfoObservers.insert(this); +} + +LLUrlEntryParcel::~LLUrlEntryParcel() +{ +	sParcelInfoObservers.erase(this);  }  std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)  { +	LLSD path_array = LLURI(url).pathArray(); +	S32 path_parts = path_array.size(); + +	if (path_parts < 3) // no parcel id +	{ +		llwarns << "Failed to parse url [" << url << "]" << llendl; +		return url; +	} + +	std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id + +	// Add an observer to call LLUrlLabelCallback when we have parcel name. +	addObserver(parcel_id_string, url, cb); + +	LLUUID parcel_id(parcel_id_string); + +	sendParcelInfoRequest(parcel_id); +  	return unescapeUrl(url);  } +void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id) +{ +	if (sRegionHost == LLHost::invalid || sDisconnected) return; + +	LLMessageSystem *msg = gMessageSystem; +	msg->newMessage("ParcelInfoRequest"); +	msg->nextBlockFast(_PREHASH_AgentData); +	msg->addUUIDFast(_PREHASH_AgentID, sAgentID ); +	msg->addUUID("SessionID", sSessionID); +	msg->nextBlock("Data"); +	msg->addUUID("ParcelID", parcel_id); +	msg->sendReliable(sRegionHost); +} + +void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label) +{ +	callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon); +} + +// static +void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) +{ +	std::string label(LLStringUtil::null); +	if (!parcel_data.name.empty()) +	{ +		label = parcel_data.name; +	} +	// If parcel name is empty use Sim_name (x, y, z) for parcel label. +	else if (!parcel_data.sim_name.empty()) +	{ +		S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; +		S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; +		S32 region_z = llround(parcel_data.global_z); + +		label = llformat("%s (%d, %d, %d)", +				parcel_data.sim_name.c_str(), region_x, region_y, region_z); +	} + +	for (std::set<LLUrlEntryParcel*>::iterator iter = sParcelInfoObservers.begin(); +			iter != sParcelInfoObservers.end(); +			++iter) +	{ +		LLUrlEntryParcel* url_entry = *iter; +		if (url_entry) +		{ +			url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label); +		} +	} +} +  //  // LLUrlEntryPlace Describes secondlife://<location> URLs  // @@ -607,6 +889,69 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const  }  // +// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. +// secondlife:///app/region/Ahern/128/128/0 +// +LLUrlEntryRegion::LLUrlEntryRegion() +{ +	mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?", +							boost::regex::perl|boost::regex::icase); +	mMenuName = "menu_url_slurl.xml"; +	mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ +	// +	// we handle SLURLs in the following formats: +	//   - secondlife:///app/region/Place/X/Y/Z +	//   - secondlife:///app/region/Place/X/Y +	//   - secondlife:///app/region/Place/X +	//   - secondlife:///app/region/Place +	// + +	LLSD path_array = LLURI(url).pathArray(); +	S32 path_parts = path_array.size(); + +	if (path_parts < 3) // no region name +	{ +		llwarns << "Failed to parse url [" << url << "]" << llendl; +		return url; +	} + +	std::string label = unescapeUrl(path_array[2]); // region name + +	if (path_parts > 3) // secondlife:///app/region/Place/X +	{ +		std::string x = path_array[3]; +		label += " (" + x; + +		if (path_parts > 4) // secondlife:///app/region/Place/X/Y +		{ +			std::string y = path_array[4]; +			label += "," + y; + +			if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z +			{ +				std::string z = path_array[5]; +				label = label + "," + z; +			} +		} + +		label += ")"; +	} + +	return label; +} + +std::string LLUrlEntryRegion::getLocation(const std::string &url) const +{ +	LLSD path_array = LLURI(url).pathArray(); +	std::string region_name = unescapeUrl(path_array[2]); +	return region_name; +} + +//  // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,  // secondlife:///app/teleport/Ahern/50/50/50/  // x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ @@ -789,9 +1134,8 @@ std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const  //  LLUrlEntryNoLink::LLUrlEntryNoLink()  { -	mPattern = boost::regex("<nolink>[^<]*</nolink>", +	mPattern = boost::regex("<nolink>.*</nolink>",  							boost::regex::perl|boost::regex::icase); -	mDisabledLink = true;  }  std::string LLUrlEntryNoLink::getUrl(const std::string &url) const @@ -805,6 +1149,13 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC  	return getUrl(url);  } +LLStyle::Params LLUrlEntryNoLink::getStyle() const  +{  +	// Don't render as URL (i.e. no context menu or hand cursor). +	return LLStyle::Params().is_link(false); +} + +  //  // LLUrlEntryIcon describes an icon with <icon>...</icon> tags  // @@ -812,7 +1163,6 @@ LLUrlEntryIcon::LLUrlEntryIcon()  {  	mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>",  							boost::regex::perl|boost::regex::icase); -	mDisabledLink = true;  }  std::string LLUrlEntryIcon::getUrl(const std::string &url) const diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index e25eaa7555..5f82721c0f 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -30,13 +30,20 @@  #include "lluuid.h"  #include "lluicolor.h" +#include "llstyle.h" + +#include "llhost.h" // for resolving parcel name by parcel id +  #include <boost/signals2.hpp>  #include <boost/regex.hpp>  #include <string>  #include <map> +class LLAvatarName; +  typedef boost::signals2::signal<void (const std::string& url, -									  const std::string& label)> LLUrlLabelSignal; +									  const std::string& label, +									  const std::string& icon)> LLUrlLabelSignal;  typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback;  /// @@ -71,10 +78,10 @@ public:  	virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }  	/// Return an icon that can be displayed next to Urls of this type -	virtual std::string getIcon(const std::string &url) { return mIcon; } +	virtual std::string getIcon(const std::string &url); -	/// Return the color to render the displayed text -	LLUIColor getColor() const { return mColor; } +	/// Return the style to render the displayed text +	virtual LLStyle::Params getStyle() const;  	/// Given a matched Url, return a tooltip string for the hyperlink  	virtual std::string getTooltip(const std::string &string) const { return mTooltip; } @@ -85,14 +92,13 @@ public:  	/// Return the name of a SL location described by this Url, if any  	virtual std::string getLocation(const std::string &url) const { return ""; } -	/// is this a match for a URL that should not be hyperlinked? -	bool isLinkDisabled() const { return mDisabledLink; } -  	/// Should this link text be underlined only when mouse is hovered over it?  	virtual bool underlineOnHoverOnly(const std::string &string) const { return false; }  	virtual LLUUID	getID(const std::string &string) const { return LLUUID::null; } +	bool isLinkDisabled() const; +  protected:  	std::string getIDStringFromUrl(const std::string &url) const;  	std::string escapeUrl(const std::string &url) const; @@ -100,7 +106,7 @@ protected:  	std::string getLabelFromWikiLink(const std::string &url) const;  	std::string getUrlFromWikiLink(const std::string &string) const;  	void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb);  -	void callObservers(const std::string &id, const std::string &label); +	virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon);  	typedef struct {  		std::string url; @@ -111,9 +117,7 @@ protected:  	std::string                                    	mIcon;  	std::string                                    	mMenuName;  	std::string                                    	mTooltip; -	LLUIColor										mColor;  	std::multimap<std::string, LLUrlEntryObserver>	mObservers; -	bool                                            mDisabledLink;  };  /// @@ -134,6 +138,7 @@ class LLUrlEntryHTTPLabel : public LLUrlEntryBase  public:  	LLUrlEntryHTTPLabel();  	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +	/*virtual*/ std::string getTooltip(const std::string &string) const;  	/*virtual*/ std::string getUrl(const std::string &string) const;  }; @@ -162,18 +167,78 @@ public:  ///  /// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,  /// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about -///  class LLUrlEntryAgent : public LLUrlEntryBase  {  public:  	LLUrlEntryAgent();  	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +	/*virtual*/ std::string getIcon(const std::string &url);  	/*virtual*/ std::string getTooltip(const std::string &string) const; +	/*virtual*/ LLStyle::Params getStyle() const;  	/*virtual*/ LLUUID	getID(const std::string &string) const;  	/*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +protected: +	/*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); +private: +	void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +}; + +/// +/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +/// that displays various forms of user name +/// This is a base class for the various implementations of name display +class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable +{ +public: +	LLUrlEntryAgentName(); +	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +	/*virtual*/ LLStyle::Params getStyle() const; +protected: +	// override this to pull out relevant name fields +	virtual std::string getName(const LLAvatarName& avatar_name) = 0;  private: -	void onAgentNameReceived(const LLUUID& id, const std::string& first, -							 const std::string& last, BOOL is_group); +	void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +}; + + +/// +/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +/// that displays the full display name + user name for an avatar +/// such as "James Linden (james.linden)" +class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName +{ +public: +	LLUrlEntryAgentCompleteName(); +private: +	/*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +/// that displays the just the display name for an avatar +/// such as "James Linden" +class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName +{ +public: +	LLUrlEntryAgentDisplayName(); +private: +	/*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +/// that displays the just the display name for an avatar +/// such as "james.linden" +class LLUrlEntryAgentUserName : public LLUrlEntryAgentName +{ +public: +	LLUrlEntryAgentUserName(); +private: +	/*virtual*/ std::string getName(const LLAvatarName& avatar_name);  };  /// @@ -185,10 +250,10 @@ class LLUrlEntryGroup : public LLUrlEntryBase  public:  	LLUrlEntryGroup();  	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +	/*virtual*/ LLStyle::Params getStyle() const;  	/*virtual*/ LLUUID	getID(const std::string &string) const;  private: -	void onGroupNameReceived(const LLUUID& id, const std::string& first, -							 const std::string& last, BOOL is_group); +	void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group);  };  /// @@ -223,8 +288,44 @@ private:  class LLUrlEntryParcel : public LLUrlEntryBase  {  public: +	struct LLParcelData +	{ +		LLUUID		parcel_id; +		std::string	name; +		std::string	sim_name; +		F32			global_x; +		F32			global_y; +		F32			global_z; +	}; +  	LLUrlEntryParcel(); +	~LLUrlEntryParcel();  	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + +	// Sends a parcel info request to sim. +	void sendParcelInfoRequest(const LLUUID& parcel_id); + +	// Calls observers of certain parcel id providing them with parcel label. +	void onParcelInfoReceived(const std::string &id, const std::string &label); + +	// Processes parcel label and triggers notifying observers. +	static void processParcelInfo(const LLParcelData& parcel_data); + +	// Next 4 setters are used to update agent and viewer connection information +	// upon events like user login, viewer disconnect and user changing region host. +	// These setters are made public to be accessible from newview and should not be +	// used in other cases. +	static void setAgentID(const LLUUID& id) { sAgentID = id; } +	static void setSessionID(const LLUUID& id) { sSessionID = id; } +	static void setRegionHost(const LLHost& host) { sRegionHost = host; } +	static void setDisconnected(bool disconnected) { sDisconnected = disconnected; } + +private: +	static LLUUID						sAgentID; +	static LLUUID						sSessionID; +	static LLHost						sRegionHost; +	static bool							sDisconnected; +	static std::set<LLUrlEntryParcel*>	sParcelInfoObservers;  };  /// @@ -240,6 +341,18 @@ public:  };  /// +/// LLUrlEntryRegion Describes a Second Life location Url, e.g., +/// secondlife:///app/region/Ahern/128/128/0 +/// +class LLUrlEntryRegion : public LLUrlEntryBase +{ +public: +	LLUrlEntryRegion(); +	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +	/*virtual*/ std::string getLocation(const std::string &url) const; +}; + +///  /// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,  /// secondlife:///app/teleport/Ahern/50/50/50/  /// @@ -297,6 +410,7 @@ public:  	LLUrlEntryNoLink();  	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);  	/*virtual*/ std::string getUrl(const std::string &string) const; +	/*virtual*/ LLStyle::Params getStyle() const;  };  /// diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index e53b0c4370..c1f1382a9f 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -37,16 +37,15 @@ LLUrlMatch::LLUrlMatch() :  	mIcon(""),  	mMenuName(""),  	mLocation(""), -	mDisabledLink(false),  	mUnderlineOnHoverOnly(false)  {  }  void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,  						   const std::string &label, const std::string &tooltip, -						   const std::string &icon, const LLUIColor& color, +						   const std::string &icon, const LLStyle::Params& style,  						   const std::string &menu, const std::string &location, -						   bool disabled_link, const LLUUID& id, bool underline_on_hover_only) +						   const LLUUID& id, bool underline_on_hover_only)  {  	mStart = start;  	mEnd = end; @@ -54,10 +53,10 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,  	mLabel = label;  	mTooltip = tooltip;  	mIcon = icon; -	mColor = color; +	mStyle = style; +	mStyle.link_href = url;  	mMenuName = menu;  	mLocation = location; -	mDisabledLink = disabled_link;  	mID = id;  	mUnderlineOnHoverOnly = underline_on_hover_only;  } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index d1b2112ee7..2818f45207 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -28,11 +28,11 @@  #ifndef LL_LLURLMATCH_H  #define LL_LLURLMATCH_H -#include "linden_common.h" +//#include "linden_common.h"  #include <string>  #include <vector> -#include "lluicolor.h" +#include "llstyle.h"  ///  /// LLUrlMatch describes a single Url that was matched within a string by  @@ -69,7 +69,7 @@ public:  	std::string getIcon() const { return mIcon; }  	/// Return the color to render the displayed text -	LLUIColor getColor() const { return mColor; } +	LLStyle::Params getStyle() const { return mStyle; }  	/// Return the name of a XUI file containing the context menu items  	std::string getMenuName() const { return mMenuName; } @@ -77,21 +77,17 @@ public:  	/// return the SL location that this Url describes, or "" if none.  	std::string getLocation() const { return mLocation; } -	/// is this a match for a URL that should not be hyperlinked? -	bool isLinkDisabled() const { return mDisabledLink; } -  	/// Should this link text be underlined only when mouse is hovered over it?  	bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; }  	/// Change the contents of this match object (used by LLUrlRegistry)  	void setValues(U32 start, U32 end, const std::string &url, const std::string &label,  	               const std::string &tooltip, const std::string &icon, -				   const LLUIColor& color, const std::string &menu,  -				   const std::string &location, bool disabled_link -				   , const LLUUID& id, bool underline_on_hover_only  = false ); - -	const LLUUID& getID() const { return mID;} +				   const LLStyle::Params& style, const std::string &menu,  +				   const std::string &location, const LLUUID& id, +				   bool underline_on_hover_only = false ); +	const LLUUID& getID() const { return mID; }  private:  	U32         mStart;  	U32         mEnd; @@ -101,10 +97,8 @@ private:  	std::string mIcon;  	std::string mMenuName;  	std::string mLocation; -  	LLUUID		mID; -	LLUIColor	mColor; -	bool        mDisabledLink; +	LLStyle::Params mStyle;  	bool		mUnderlineOnHoverOnly;  }; diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 9d215cf7ef..523ee5d78c 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -31,22 +31,30 @@  #include <boost/regex.hpp>  // default dummy callback that ignores any label updates from the server -void LLUrlRegistryNullCallback(const std::string &url, const std::string &label) +void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, const std::string& icon)  {  }  LLUrlRegistry::LLUrlRegistry()  { +	mUrlEntry.reserve(20); +  	// Urls are matched in the order that they were registered  	registerUrl(new LLUrlEntryNoLink());  	registerUrl(new LLUrlEntryIcon());  	registerUrl(new LLUrlEntrySLURL());  	registerUrl(new LLUrlEntryHTTP());  	registerUrl(new LLUrlEntryHTTPLabel()); +	registerUrl(new LLUrlEntryAgentCompleteName()); +	registerUrl(new LLUrlEntryAgentDisplayName()); +	registerUrl(new LLUrlEntryAgentUserName()); +	// LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since  +	// LLUrlEntryAgent is a less specific (catchall for agent urls)  	registerUrl(new LLUrlEntryAgent());  	registerUrl(new LLUrlEntryGroup());  	registerUrl(new LLUrlEntryParcel());  	registerUrl(new LLUrlEntryTeleport()); +	registerUrl(new LLUrlEntryRegion());  	registerUrl(new LLUrlEntryWorldMap());  	registerUrl(new LLUrlEntryObjectIM());  	registerUrl(new LLUrlEntryPlace()); @@ -71,10 +79,13 @@ LLUrlRegistry::~LLUrlRegistry()  	}  } -void LLUrlRegistry::registerUrl(LLUrlEntryBase *url) +void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front)  {  	if (url)  	{ +		if (force_front)  // IDEVO +			mUrlEntry.insert(mUrlEntry.begin(), url); +		else  		mUrlEntry.push_back(url);  	}  } @@ -174,10 +185,9 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL  						match_entry->getLabel(url, cb),  						match_entry->getTooltip(url),  						match_entry->getIcon(url), -						match_entry->getColor(), +						match_entry->getStyle(),  						match_entry->getMenuName(),  						match_entry->getLocation(url), -						match_entry->isLinkDisabled(),  						match_entry->getID(url),  						match_entry->underlineOnHoverOnly(url));  		return true; @@ -210,10 +220,9 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr  						match.getLabel(),  						match.getTooltip(),  						match.getIcon(), -						match.getColor(), +						match.getStyle(),  						match.getMenuName(),  						match.getLocation(), -						match.isLinkDisabled(),  						match.getID(),  						match.underlineOnHoverOnly());  		return true; diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h index 24ce516c43..da16171a97 100644 --- a/indra/llui/llurlregistry.h +++ b/indra/llui/llurlregistry.h @@ -37,7 +37,9 @@  #include <vector>  /// This default callback for findUrl() simply ignores any label updates -void LLUrlRegistryNullCallback(const std::string &url, const std::string &label); +void LLUrlRegistryNullCallback(const std::string &url, +							   const std::string &label, +							   const std::string &icon);  ///  /// LLUrlRegistry is a singleton that contains a set of Url types that @@ -64,7 +66,9 @@ public:  	~LLUrlRegistry();  	/// add a new Url handler to the registry (will be freed on destruction) -	void registerUrl(LLUrlEntryBase *url); +	/// optionally force it to the front of the list, making it take +	/// priority over other regular expression matches for URLs +	void registerUrl(LLUrlEntryBase *url, bool force_front = false);  	/// get the next Url in an input string, starting at a given character offset  	/// your callback is invoked if the matched Url's label changes in the future diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 3fa86bf0ca..267640a226 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -102,6 +102,7 @@ LLView::Params::Params()  	left_pad("left_pad"),  	left_delta("left_delta", S32_MAX),  	from_xui("from_xui", false), +	focus_root("focus_root", false),  	needs_translate("translate"),  	xmlns("xmlns"),  	xmlns_xsi("xmlns:xsi"), @@ -117,7 +118,7 @@ LLView::LLView(const LLView::Params& p)  	mParentView(NULL),  	mReshapeFlags(FOLLOWS_NONE),  	mFromXUI(p.from_xui), -	mIsFocusRoot(FALSE), +	mIsFocusRoot(p.focus_root),  	mLastVisible(FALSE),  	mNextInsertionOrdinal(0),  	mHoverCursor(getCursorFromString(p.hover_cursor)), @@ -163,8 +164,6 @@ LLView::~LLView()  	if (mDefaultWidgets)  	{ -		std::for_each(mDefaultWidgets->begin(), mDefaultWidgets->end(), -					  DeletePairedPointer());  		delete mDefaultWidgets;  		mDefaultWidgets = NULL;  	} @@ -1682,18 +1681,7 @@ BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const  //-----------------------------------------------------------------------------  LLView* LLView::getChildView(const std::string& name, BOOL recurse) const  { -	LLView* child = findChildView(name, recurse); -	if (!child) -	{ -		child = getDefaultWidget<LLView>(name); -		if (!child) -		{ -			LLView::Params view_params; -			view_params.name = name; -			child = LLUICtrlFactory::create<LLView>(view_params); -		} -	} -	return child; +	return getChild<LLView>(name, recurse);  }  static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets"); @@ -2804,11 +2792,14 @@ LLView::root_to_view_iterator_t LLView::endRootToView()  // only create maps on demand, as they incur heap allocation/deallocation cost  // when a view is constructed/deconstructed -LLView::default_widget_map_t& LLView::getDefaultWidgetMap() const +LLView& LLView::getDefaultWidgetContainer() const  {  	if (!mDefaultWidgets)  	{ -		mDefaultWidgets = new default_widget_map_t(); +		LLView::Params p; +		p.name = "default widget container"; +		p.visible = false; // ensures default widgets can't steal focus, etc. +		mDefaultWidgets = new LLView(p);  	}  	return *mDefaultWidgets;  } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 33d345beff..d2bbd663b8 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -116,7 +116,8 @@ public:  									visible,  									mouse_opaque,  									use_bounding_rect, -									from_xui; +									from_xui, +									focus_root;  		Optional<S32>				tab_group,  									default_tab_group; @@ -412,14 +413,9 @@ public:  	LLControlVariable *findControl(const std::string& name); -    // Moved setValue(), getValue(), setControlValue(), setControlName(), -    // controlListener() to LLUICtrl because an LLView is NOT assumed to -    // contain a value. If that's what you want, use LLUICtrl instead. -//	virtual bool	handleEvent(LLPointer<LLEvent> event, const LLSD& userdata); -  	const child_list_t*	getChildList() const { return &mChildList; } -	const child_list_const_iter_t	beginChild()  { return mChildList.begin(); } -	const child_list_const_iter_t	endChild()  { return mChildList.end(); } +	child_list_const_iter_t	beginChild() const { return mChildList.begin(); } +	child_list_const_iter_t	endChild() const { return mChildList.end(); }  	// LLMouseHandler functions  	//  Default behavior is to pass events to children @@ -466,12 +462,8 @@ public:  	template <class T> T* getDefaultWidget(const std::string& name) const  	{ -		default_widget_map_t::const_iterator found_it = getDefaultWidgetMap().find(name); -		if (found_it == getDefaultWidgetMap().end()) -		{ -			return NULL; -		} -		return dynamic_cast<T*>(found_it->second); +		LLView* widgetp = getDefaultWidgetContainer().findChildView(name); +		return dynamic_cast<T*>(widgetp);  	}  	////////////////////////////////////////////// @@ -585,9 +577,9 @@ private:  	typedef std::map<std::string, LLView*> default_widget_map_t;  	// allocate this map no demand, as it is rarely needed -	mutable default_widget_map_t* mDefaultWidgets; +	mutable LLView* mDefaultWidgets; -	default_widget_map_t& getDefaultWidgetMap() const; +	LLView& getDefaultWidgetContainer() const;  public:  	// Depth in view hierarchy during rendering @@ -654,7 +646,7 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co  				return NULL;  			} -			getDefaultWidgetMap()[name] = result; +			getDefaultWidgetContainer().addChild(result);  		}  	}  	return result; diff --git a/indra/llui/llwindowshade.cpp b/indra/llui/llwindowshade.cpp new file mode 100644 index 0000000000..77e94385d4 --- /dev/null +++ b/indra/llui/llwindowshade.cpp @@ -0,0 +1,328 @@ +/** + * @file LLWindowShade.cpp + * @brief Notification dialog that slides down and optionally disabled a piece of UI + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llwindowshade.h" + +#include "lllayoutstack.h" +#include "lltextbox.h" +#include "lliconctrl.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "lllineeditor.h" + +const S32 MIN_NOTIFICATION_AREA_HEIGHT = 30; +const S32 MAX_NOTIFICATION_AREA_HEIGHT = 100; + +LLWindowShade::Params::Params() +:	bg_image("bg_image"), +	modal("modal", false), +	text_color("text_color"), +	can_close("can_close", true) +{ +	mouse_opaque = false; +} + +LLWindowShade::LLWindowShade(const LLWindowShade::Params& params) +:	LLUICtrl(params), +	mNotification(params.notification), +	mModal(params.modal), +	mFormHeight(0), +	mTextColor(params.text_color) +{ +	setFocusRoot(true); +} + +void LLWindowShade::initFromParams(const LLWindowShade::Params& params) +{ +	LLUICtrl::initFromParams(params); + +	LLLayoutStack::Params layout_p; +	layout_p.name = "notification_stack"; +	layout_p.rect = params.rect; +	layout_p.follows.flags = FOLLOWS_ALL; +	layout_p.mouse_opaque = false; +	layout_p.orientation = LLLayoutStack::VERTICAL; +	layout_p.border_size = 0; + +	LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p); +	addChild(stackp); + +	LLLayoutPanel::Params panel_p; +	panel_p.rect = LLRect(0, 30, 800, 0); +	panel_p.name = "notification_area"; +	panel_p.visible = false; +	panel_p.user_resize = false; +	panel_p.background_visible = true; +	panel_p.bg_alpha_image = params.bg_image; +	panel_p.auto_resize = false; +	LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); +	stackp->addChild(notification_panel); + +	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); +	panel_p.auto_resize = true; +	panel_p.user_resize = false; +	panel_p.rect = params.rect; +	panel_p.name = "background_area"; +	panel_p.mouse_opaque = false; +	panel_p.background_visible = false; +	panel_p.bg_alpha_color = LLColor4(0.f, 0.f, 0.f, 0.2f); +	LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); +	stackp->addChild(dummy_panel); + +	layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>(); +	layout_p.rect = LLRect(0, 30, 800, 0); +	layout_p.follows.flags = FOLLOWS_ALL; +	layout_p.orientation = LLLayoutStack::HORIZONTAL; +	stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p); +	notification_panel->addChild(stackp); + +	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); +	panel_p.rect.height = 30; +	LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); +	stackp->addChild(panel); + +	LLIconCtrl::Params icon_p; +	icon_p.name = "notification_icon"; +	icon_p.rect = LLRect(5, 23, 21, 8); +	panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p)); + +	LLTextBox::Params text_p; +	text_p.rect = LLRect(31, 20, panel->getRect().getWidth() - 5, 0); +	text_p.follows.flags = FOLLOWS_ALL; +	text_p.text_color = mTextColor; +	text_p.font = LLFontGL::getFontSansSerifSmall(); +	text_p.font.style = "BOLD"; +	text_p.name = "notification_text"; +	text_p.use_ellipses = true; +	text_p.wrap = true; +	panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p)); + +	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); +	panel_p.auto_resize = false; +	panel_p.user_resize = false; +	panel_p.name="form_elements"; +	panel_p.rect = LLRect(0, 30, 130, 0); +	LLLayoutPanel* form_elements_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); +	stackp->addChild(form_elements_panel); + +	if (params.can_close) +	{ +		panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); +		panel_p.auto_resize = false; +		panel_p.user_resize = false; +		panel_p.rect = LLRect(0, 30, 25, 0); +		LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); +		stackp->addChild(close_panel); + +		LLButton::Params button_p; +		button_p.name = "close_notification"; +		button_p.rect = LLRect(5, 23, 21, 7); +		button_p.image_color.control="DkGray_66"; +		button_p.image_unselected.name="Icon_Close_Foreground"; +		button_p.image_selected.name="Icon_Close_Press"; +		button_p.click_callback.function = boost::bind(&LLWindowShade::onCloseNotification, this); + +		close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p)); +	} + +	LLSD payload = mNotification->getPayload(); + +	LLNotificationFormPtr formp = mNotification->getForm(); +	LLLayoutPanel& notification_area = getChildRef<LLLayoutPanel>("notification_area"); +	notification_area.getChild<LLUICtrl>("notification_icon")->setValue(mNotification->getIcon()); +	notification_area.getChild<LLUICtrl>("notification_text")->setValue(mNotification->getMessage()); +	notification_area.getChild<LLUICtrl>("notification_text")->setToolTip(mNotification->getMessage()); + +	LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType();  +	LLLayoutPanel& form_elements = notification_area.getChildRef<LLLayoutPanel>("form_elements"); +	form_elements.deleteAllChildren(); + +	const S32 FORM_PADDING_HORIZONTAL = 10; +	const S32 FORM_PADDING_VERTICAL = 3; +	const S32 WIDGET_HEIGHT = 24; +	const S32 LINE_EDITOR_WIDTH = 120; +	S32 cur_x = FORM_PADDING_HORIZONTAL; +	S32 cur_y = FORM_PADDING_VERTICAL + WIDGET_HEIGHT; +	S32 form_width = cur_x; + +	if (ignore_type != LLNotificationForm::IGNORE_NO) +	{ +		LLCheckBoxCtrl::Params checkbox_p; +		checkbox_p.name = "ignore_check"; +		checkbox_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT); +		checkbox_p.label = formp->getIgnoreMessage(); +		checkbox_p.label_text.text_color = LLColor4::black; +		checkbox_p.commit_callback.function = boost::bind(&LLWindowShade::onClickIgnore, this, _1); +		checkbox_p.initial_value = formp->getIgnored(); + +		LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p); +		check->setRect(check->getBoundingRect()); +		form_elements.addChild(check); +		cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL; +		form_width = llmax(form_width, cur_x); +	} + +	for (S32 i = 0; i < formp->getNumElements(); i++) +	{ +		LLSD form_element = formp->getElement(i); +		std::string type = form_element["type"].asString(); +		if (type == "button") +		{ +			LLButton::Params button_p; +			button_p.name = form_element["name"]; +			button_p.label = form_element["text"]; +			button_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT); +			button_p.click_callback.function = boost::bind(&LLWindowShade::onClickNotificationButton, this, form_element["name"].asString()); +			button_p.auto_resize = true; + +			LLButton* button = LLUICtrlFactory::create<LLButton>(button_p); +			button->autoResize(); +			form_elements.addChild(button); + +			if (form_element["default"].asBoolean()) +			{ +				form_elements.setDefaultBtn(button); +			} + +			cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL; +			form_width = llmax(form_width, cur_x); +		} +		else if (type == "text" || type == "password") +		{ +			// if not at beginning of line... +			if (cur_x != FORM_PADDING_HORIZONTAL) +			{ +				// start new line +				cur_x = FORM_PADDING_HORIZONTAL; +				cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL; +			} +			LLTextBox::Params label_p; +			label_p.name = form_element["name"].asString() + "_label"; +			label_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT); +			label_p.initial_value = form_element["text"]; +			label_p.text_color = mTextColor; +			label_p.font_valign = LLFontGL::VCENTER; +			label_p.v_pad = 5; +			LLTextBox* textbox = LLUICtrlFactory::create<LLTextBox>(label_p); +			textbox->reshapeToFitText(); +			textbox->reshape(textbox->getRect().getWidth(), form_elements.getRect().getHeight() - 2 * FORM_PADDING_VERTICAL);  +			form_elements.addChild(textbox); +			cur_x = textbox->getRect().mRight + FORM_PADDING_HORIZONTAL; + +			LLLineEditor::Params line_p; +			line_p.name = form_element["name"]; +			line_p.keystroke_callback = boost::bind(&LLWindowShade::onEnterNotificationText, this, _1, form_element["name"].asString()); +			line_p.is_password = type == "password"; +			line_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT); + +			LLLineEditor* line_editor = LLUICtrlFactory::create<LLLineEditor>(line_p); +			form_elements.addChild(line_editor); +			form_width = llmax(form_width, cur_x + LINE_EDITOR_WIDTH + FORM_PADDING_HORIZONTAL); + +			// reset to start of next line +			cur_x = FORM_PADDING_HORIZONTAL; +			cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL; +		} +	} + +	mFormHeight = form_elements.getRect().getHeight() - (cur_y - FORM_PADDING_VERTICAL) + WIDGET_HEIGHT; +	form_elements.reshape(form_width, mFormHeight); +	form_elements.setMinDim(form_width); + +	// move all form elements back onto form surface +	S32 delta_y = WIDGET_HEIGHT + FORM_PADDING_VERTICAL - cur_y; +	for (child_list_const_iter_t it = form_elements.getChildList()->begin(), end_it = form_elements.getChildList()->end(); +		it != end_it; +		++it) +	{ +		(*it)->translate(0, delta_y); +	} +} + +void LLWindowShade::show() +{ +	getChildRef<LLLayoutPanel>("notification_area").setVisible(true); +	getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(mModal); + +	setMouseOpaque(mModal); +} + +void LLWindowShade::draw() +{ +	LLRect message_rect = getChild<LLTextBox>("notification_text")->getTextBoundingRect(); + +	LLLayoutPanel* notification_area = getChild<LLLayoutPanel>("notification_area"); + +	notification_area->reshape(notification_area->getRect().getWidth(),  +		llclamp(message_rect.getHeight() + 10,  +				llmin(mFormHeight, MAX_NOTIFICATION_AREA_HEIGHT), +				MAX_NOTIFICATION_AREA_HEIGHT)); + +	LLUICtrl::draw(); +	if (mNotification && !mNotification->isActive()) +	{ +		hide(); +	} +} + +void LLWindowShade::hide() +{ +	getChildRef<LLLayoutPanel>("notification_area").setVisible(false); +	getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false); + +	setMouseOpaque(false); +} + +void LLWindowShade::onCloseNotification() +{ +	LLNotifications::instance().cancel(mNotification); +} + +void LLWindowShade::onClickIgnore(LLUICtrl* ctrl) +{ +	bool check = ctrl->getValue().asBoolean(); +	if (mNotification && mNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN) +	{ +		// question was "show again" so invert value to get "ignore" +		check = !check; +	} +	mNotification->setIgnored(check); +} + +void LLWindowShade::onClickNotificationButton(const std::string& name) +{ +	if (!mNotification) return; + +	mNotificationResponse[name] = true; + +	mNotification->respond(mNotificationResponse); +} + +void LLWindowShade::onEnterNotificationText(LLUICtrl* ctrl, const std::string& name) +{ +	mNotificationResponse[name] = ctrl->getValue().asString(); +} diff --git a/indra/llui/llwindowshade.h b/indra/llui/llwindowshade.h new file mode 100644 index 0000000000..0047195929 --- /dev/null +++ b/indra/llui/llwindowshade.h @@ -0,0 +1,69 @@ +/** + * @file llwindowshade.h + * @brief Notification dialog that slides down and optionally disabled a piece of UI + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWSHADE_H +#define LL_LLWINDOWSHADE_H + +#include "lluictrl.h" +#include "llnotifications.h" + +class LLWindowShade : public LLUICtrl +{ +public: +	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> +	{ +		Mandatory<LLNotificationPtr>	notification; +		Optional<LLUIImage*>			bg_image; +		Optional<LLUIColor>				text_color; +		Optional<bool>					modal, +										can_close; + +		Params(); +	}; + +	void show(); +	/*virtual*/ void draw(); +	void hide(); + +private: +	friend class LLUICtrlFactory; + +	LLWindowShade(const Params& p); +	void initFromParams(const Params& params); + +	void onCloseNotification(); +	void onClickNotificationButton(const std::string& name); +	void onEnterNotificationText(LLUICtrl* ctrl, const std::string& name); +	void onClickIgnore(LLUICtrl* ctrl); + +	LLNotificationPtr	mNotification; +	LLSD				mNotificationResponse; +	bool				mModal; +	S32					mFormHeight; +	LLUIColor			mTextColor; +}; + +#endif // LL_LLWINDOWSHADE_H diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index ff53ae5624..96ebe83826 100644 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -27,11 +27,29 @@  #include "llstring.h"  #include "llfile.h" +#include "llavatarnamecache.h"  #include "llcachename.h"  #include "lluuid.h" +#include "message.h"  #include <string> +// Stub for LLAvatarNameCache +bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) +{ +	return false; +} + +void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) +{ +	return; +} + +bool LLAvatarNameCache::useDisplayNames() +{ +	return false; +} +  //  // Stub implementation for LLCacheName  // @@ -47,7 +65,12 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)  	return TRUE;  } -boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback) +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback) +{ +	return boost::signals2::connection(); +} + +boost::signals2::connection LLCacheName::getGroup(const LLUUID& id, const LLCacheNameCallback& callback)  {  	return boost::signals2::connection();  } @@ -67,3 +90,122 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::  {  	return std::string();  } + +// +// Stub implementation for LLStyle::Params::Params +// + +LLStyle::Params::Params() +{ +} + +// +// Stub implementations for various LLInitParam classes +// + +namespace LLInitParam +{ +	BaseBlock::BaseBlock() {} +	BaseBlock::~BaseBlock() {} +	Param::Param(BaseBlock* enclosing_block) +	:	mIsProvided(false) +	{ +		const U8* my_addr = reinterpret_cast<const U8*>(this); +		const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); +		mEnclosingBlockOffset = (U16)(my_addr - block_addr); +	} +	void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided) {} + +	void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name){} +	param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;} +	 +	void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) +	{ +		descriptor.mCurrentBlockPtr = this; +	} +	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack){ return true; } +	bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const { return true; } +	bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const { return true; } +	bool BaseBlock::merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; } +	bool BaseBlock::validateBlock(bool emit_errors) const { return true; } + +	TypedParam<LLUIColor >::TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) +	:	super_t(descriptor, name, value, func, min_count, max_count) +	{} + +	void TypedParam<LLUIColor>::setValueFromBlock() const +	{} +	 +	void TypedParam<LLUIColor>::setBlockFromValue() +	{} + +	void TypeValues<LLUIColor>::declareValues() +	{} + +	bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b) +	{ +		return false; +	} + +	TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) +	:	super_t(descriptor, _name, value, func, min_count, max_count) +	{} + +	void TypedParam<const LLFontGL*>::setValueFromBlock() const +	{} +	 +	void TypedParam<const LLFontGL*>::setBlockFromValue() +	{} + +	void TypeValues<LLFontGL::HAlign>::declareValues() +	{} + +	void TypeValues<LLFontGL::VAlign>::declareValues() +	{} + +	void TypeValues<LLFontGL::ShadowType>::declareValues() +	{} + +	void TypedParam<LLUIImage*>::setValueFromBlock() const +	{} +	 +	void TypedParam<LLUIImage*>::setBlockFromValue() +	{} + +	 +	bool ParamCompare<LLUIImage*, false>::equals( +		LLUIImage* const &a, +		LLUIImage* const &b) +	{ +		return false; +	} + +	bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b) +	{ +		return false; +	} + +} + +//static +LLFontGL* LLFontGL::getFontDefault() +{ +	return NULL;  +} + +char* _PREHASH_AgentData = "AgentData"; +char* _PREHASH_AgentID = "AgentID"; + +LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS); + +LLMessageSystem* gMessageSystem = NULL; + +// +// Stub implementation for LLMessageSystem +// +void LLMessageSystem::newMessage(const char *name) { } +void LLMessageSystem::nextBlockFast(const char *blockname) { } +void LLMessageSystem::nextBlock(const char *blockname) { } +void LLMessageSystem::addUUIDFast( const char *varname, const LLUUID& uuid) { } +void LLMessageSystem::addUUID( const char *varname, const LLUUID& uuid) { } +S32 LLMessageSystem::sendReliable(const LLHost &host) { return 0; } diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 95affe4460..8f0a48018f 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -27,12 +27,22 @@  #include "linden_common.h"  #include "../llurlentry.h" +#include "../lluictrl.h"  #include "llurlentry_stub.cpp"  #include "lltut.h"  #include "../lluicolortable.h" +#include "../lluiimage.h"  #include <boost/regex.hpp> +typedef std::map<std::string, LLControlGroup*> settings_map_t; +settings_map_t LLUI::sSettingGroups; + +BOOL LLControlGroup::getBOOL(const std::string& name) +{ +	return false; +} +  LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const  {  	return LLUIColor(); @@ -40,6 +50,26 @@ LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& defa  LLUIColor::LLUIColor() : mColorPtr(NULL) {} +LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image) +{ +} + +LLUIImage::~LLUIImage() +{ +} + +//virtual +S32 LLUIImage::getWidth() const +{ +	return 0; +} + +//virtual +S32 LLUIImage::getHeight() const +{ +	return 0; +} +  namespace tut  {  	struct LLUrlEntryData @@ -73,6 +103,45 @@ namespace tut  		ensure_equals(testname, url, expected);  	} +	void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) +	{ +	} + +	void testLabel(const std::string &testname, LLUrlEntryBase &entry, +				   const char *text, const std::string &expected) +	{ +		boost::regex regex = entry.getPattern(); +		std::string label = ""; +		boost::cmatch result; +		bool found = boost::regex_search(text, result, regex); +		if (found) +		{ +			S32 start = static_cast<U32>(result[0].first - text); +			S32 end = static_cast<U32>(result[0].second - text); +			std::string url = std::string(text+start, end-start); +			label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); +		} +		ensure_equals(testname, label, expected); +	} + +	void testLocation(const std::string &testname, LLUrlEntryBase &entry, +					  const char *text, const std::string &expected) +	{ +		boost::regex regex = entry.getPattern(); +		std::string location = ""; +		boost::cmatch result; +		bool found = boost::regex_search(text, result, regex); +		if (found) +		{ +			S32 start = static_cast<U32>(result[0].first - text); +			S32 end = static_cast<U32>(result[0].second - text); +			std::string url = std::string(text+start, end-start); +			location = entry.getLocation(url); +		} +		ensure_equals(testname, location, expected); +	} + +  	template<> template<>  	void object::test<1>()  	{ @@ -667,4 +736,114 @@ namespace tut  				  "<nolink>My Object</nolink>",  				  "My Object");  	} + +	template<> template<> +	void object::test<13>() +	{ +		// +		// test LLUrlEntryRegion - secondlife:///app/region/<location> URLs +		// +		LLUrlEntryRegion url; + +		// Regex tests. +		testRegex("no valid region", url, +				  "secondlife:///app/region/", +				  ""); + +		testRegex("invalid coords", url, +				  "secondlife:///app/region/Korea2/a/b/c", +				  "secondlife:///app/region/Korea2/"); // don't count invalid coords + +		testRegex("Ahern (50,50,50) [1]", url, +				  "secondlife:///app/region/Ahern/50/50/50/", +				  "secondlife:///app/region/Ahern/50/50/50/"); + +		testRegex("Ahern (50,50,50) [2]", url, +				  "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", +				  "secondlife:///app/region/Ahern/50/50/50/"); + +		testRegex("Ahern (50,50,50) [3]", url, +				  "XXX secondlife:///app/region/Ahern/50/50/50 XXX", +				  "secondlife:///app/region/Ahern/50/50/50"); + +		testRegex("Ahern (50,50,50) multicase", url, +				  "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", +				  "secondlife:///app/region/Ahern/50/50/50/"); + +		testRegex("Ahern (50,50) [1]", url, +				  "XXX secondlife:///app/region/Ahern/50/50/ XXX", +				  "secondlife:///app/region/Ahern/50/50/"); + +		testRegex("Ahern (50,50) [2]", url, +				  "XXX secondlife:///app/region/Ahern/50/50 XXX", +				  "secondlife:///app/region/Ahern/50/50"); + +		// DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat +		testRegex("Region with brackets", url, +				  "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX", +				  "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30"); + +		// DEV-35459: SLURLs and teleport Links not parsed properly +		testRegex("Region with quote", url, +				  "XXX secondlife:///app/region/A'ksha%20Oasis/41/166/701 XXX", +			          "secondlife:///app/region/A%27ksha%20Oasis/41/166/701"); + +		// Rendering tests. +		testLabel("Render /app/region/Ahern/50/50/50/", url, +			"secondlife:///app/region/Ahern/50/50/50/", +			"Ahern (50,50,50)"); + +		testLabel("Render /app/region/Ahern/50/50/50", url, +			"secondlife:///app/region/Ahern/50/50/50", +			"Ahern (50,50,50)"); + +		testLabel("Render /app/region/Ahern/50/50/", url, +			"secondlife:///app/region/Ahern/50/50/", +			"Ahern (50,50)"); + +		testLabel("Render /app/region/Ahern/50/50", url, +			"secondlife:///app/region/Ahern/50/50", +			"Ahern (50,50)"); + +		testLabel("Render /app/region/Ahern/50/", url, +			"secondlife:///app/region/Ahern/50/", +			"Ahern (50)"); + +		testLabel("Render /app/region/Ahern/50", url, +			"secondlife:///app/region/Ahern/50", +			"Ahern (50)"); + +		testLabel("Render /app/region/Ahern/", url, +			"secondlife:///app/region/Ahern/", +			"Ahern"); + +		testLabel("Render /app/region/Ahern/ within context", url, +			"XXX secondlife:///app/region/Ahern/ XXX", +			"Ahern"); + +		testLabel("Render /app/region/Ahern", url, +			"secondlife:///app/region/Ahern", +			"Ahern"); + +		testLabel("Render /app/region/Ahern within context", url, +			"XXX secondlife:///app/region/Ahern XXX", +			"Ahern"); + +		testLabel("Render /app/region/Product%20Engine/", url, +			"secondlife:///app/region/Product%20Engine/", +			"Product Engine"); + +		testLabel("Render /app/region/Product%20Engine", url, +			"secondlife:///app/region/Product%20Engine", +			"Product Engine"); + +		// Location parsing texts. +		testLocation("Location /app/region/Ahern/50/50/50/", url, +			"secondlife:///app/region/Ahern/50/50/50/", +			"Ahern"); + +		testLocation("Location /app/region/Product%20Engine", url, +			"secondlife:///app/region/Product%20Engine", +			"Product Engine"); +	}  } diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 4e38bea1bd..fdaab00f18 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -25,14 +25,135 @@   * $/LicenseInfo$   */ +#include "linden_common.h" +  #include "../llurlmatch.h" +#include "../lluiimage.h"  #include "lltut.h" -// link seam +// link seams +  LLUIColor::LLUIColor()  	: mColorPtr(NULL)  {} +LLStyle::Params::Params() +{ +} + +LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image) +{ +} + +LLUIImage::~LLUIImage() +{ +} + +//virtual +S32 LLUIImage::getWidth() const +{ +	return 0; +} + +//virtual +S32 LLUIImage::getHeight() const +{ +	return 0; +} + +namespace LLInitParam +{ +	BaseBlock::BaseBlock() {} +	BaseBlock::~BaseBlock() {} + +	void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided) {} + +	void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name){} +	param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;} +	 +	void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) +	{ +		descriptor.mCurrentBlockPtr = this; +	} + +	Param::Param(BaseBlock* enclosing_block) +	:	mIsProvided(false) +	{ +		const U8* my_addr = reinterpret_cast<const U8*>(this); +		const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); +		mEnclosingBlockOffset = (U16)(my_addr - block_addr); +	} + +	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack){ return true; } +	bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const { return true; } +	bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const { return true; } +	bool BaseBlock::merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; } +	bool BaseBlock::validateBlock(bool emit_errors) const { return true; } + +	TypedParam<LLUIColor >::TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) +	:	super_t(descriptor, name, value, func, min_count, max_count) +	{} + +	void TypedParam<LLUIColor>::setValueFromBlock() const +	{} +	 +	void TypedParam<LLUIColor>::setBlockFromValue() +	{} + +	void TypeValues<LLUIColor>::declareValues() +	{} + +	bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b) +	{ +		return false; +	} + +	TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) +	:	super_t(descriptor, _name, value, func, min_count, max_count) +	{} + +	void TypedParam<const LLFontGL*>::setValueFromBlock() const +	{} +	 +	void TypedParam<const LLFontGL*>::setBlockFromValue() +	{} + +	void TypeValues<LLFontGL::HAlign>::declareValues() +	{} + +	void TypeValues<LLFontGL::VAlign>::declareValues() +	{} + +	void TypeValues<LLFontGL::ShadowType>::declareValues() +	{} + +	void TypedParam<LLUIImage*>::setValueFromBlock() const +	{} +	 +	void TypedParam<LLUIImage*>::setBlockFromValue() +	{} +	 +	bool ParamCompare<LLUIImage*, false>::equals( +		LLUIImage* const &a, +		LLUIImage* const &b) +	{ +		return false; +	} + +	bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b) +	{ +		return false; +	} + +} + +//static +LLFontGL* LLFontGL::getFontDefault() +{ +	return NULL;  +} + +  namespace tut  {  	struct LLUrlMatchData @@ -59,7 +180,7 @@ namespace tut  		LLUrlMatch match;  		ensure("empty()", match.empty()); -		match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure("! empty()", ! match.empty());  	} @@ -72,7 +193,7 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getStart() == 0", match.getStart(), 0); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getStart() == 10", match.getStart(), 10);  	} @@ -85,7 +206,7 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getEnd() == 0", match.getEnd(), 0); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getEnd() == 20", match.getEnd(), 20);  	} @@ -98,10 +219,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getUrl() == ''", match.getUrl(), ""); -		match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "http://slurl.com/", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getUrl() == '' (2)", match.getUrl(), "");  	} @@ -114,10 +235,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getLabel() == ''", match.getLabel(), ""); -		match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "Label", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getLabel() == '' (2)", match.getLabel(), "");  	} @@ -130,10 +251,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getTooltip() == ''", match.getTooltip(), ""); -		match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "Info", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");  	} @@ -146,10 +267,10 @@ namespace tut  		LLUrlMatch match;  		ensure_equals("getIcon() == ''", match.getIcon(), ""); -		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "Icon", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure_equals("getIcon() == '' (2)", match.getIcon(), "");  	} @@ -162,10 +283,10 @@ namespace tut  		LLUrlMatch match;  		ensure("getMenuName() empty", match.getMenuName().empty()); -		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "", LLUUID::null);  		ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure("getMenuName() empty (2)", match.getMenuName().empty());  	} @@ -178,10 +299,10 @@ namespace tut  		LLUrlMatch match;  		ensure("getLocation() empty", match.getLocation().empty()); -		match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "Paris", LLUUID::null);  		ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris"); -		match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null); +		match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);  		ensure("getLocation() empty (2)", match.getLocation().empty());  	}  } | 
