PageRenderTime 97ms CodeModel.GetById 17ms app.highlight 71ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/lllocationinputctrl.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1195 lines | 899 code | 152 blank | 144 comment | 154 complexity | 2f2d0b9a89eb5dba20e323b615707951 MD5 | raw file
   1/**
   2 * @file lllocationinputctrl.cpp
   3 * @brief Combobox-like location input control
   4 *
   5 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29// file includes
  30#include "lllocationinputctrl.h"
  31
  32// common includes
  33#include "llbutton.h"
  34#include "llfocusmgr.h"
  35#include "llhelp.h"
  36#include "llmenugl.h"
  37#include "llparcel.h"
  38#include "llstring.h"
  39#include "lltrans.h"
  40#include "lluictrlfactory.h"
  41#include "lltooltip.h"
  42#include "llnotificationsutil.h"
  43#include "llregionflags.h"
  44
  45// newview includes
  46#include "llagent.h"
  47#include "llfloatersidepanelcontainer.h"
  48#include "llinventoryobserver.h"
  49#include "lllandmarkactions.h"
  50#include "lllandmarklist.h"
  51#include "llteleporthistory.h"
  52#include "llslurl.h"
  53#include "llstatusbar.h"			// getHealth()
  54#include "lltrans.h"
  55#include "llviewerinventory.h"
  56#include "llviewerparcelmgr.h"
  57#include "llviewerregion.h"
  58#include "llviewercontrol.h"
  59#include "llviewermenu.h"
  60#include "llurllineeditorctrl.h"
  61#include "llagentui.h"
  62
  63//============================================================================
  64/*
  65 * "ADD LANDMARK" BUTTON UPDATING LOGIC
  66 * 
  67 * If the current parcel has been landmarked, we should draw
  68 * a special image on the button.
  69 * 
  70 * To avoid determining the appropriate image on every draw() we do that
  71 * only in the following cases:
  72 * 1) Navbar is shown for the first time after login.
  73 * 2) Agent moves to another parcel.
  74 * 3) A landmark is created or removed.
  75 * 
  76 * The first case is handled by the handleLoginComplete() method.
  77 * 
  78 * The second case is handled by setting the "agent parcel changed" callback
  79 * on LLViewerParcelMgr.
  80 * 
  81 * The third case is the most complex one. We have two inventory observers for that:
  82 * one is designated to handle adding landmarks, the other handles removal.
  83 * Let's see how the former works.
  84 * 
  85 * When we get notified about landmark addition, the landmark position is unknown yet. What we can
  86 * do at that point is initiate loading the landmark data by LLLandmarkList and set the
  87 * "loading finished" callback on it. Finally, when the callback is triggered,
  88 * we can determine whether the landmark refers to a point within the current parcel
  89 * and choose the appropriate image for the "Add landmark" button.
  90 */
  91
  92/**
  93 * Initiates loading the landmarks that have been just added.
  94 *
  95 * Once the loading is complete we'll be notified
  96 * with the callback we set for LLLandmarkList.
  97 */
  98class LLAddLandmarkObserver : public LLInventoryAddedObserver
  99{
 100public:
 101	LLAddLandmarkObserver(LLLocationInputCtrl* input) : mInput(input) {}
 102
 103private:
 104	/*virtual*/ void done()
 105	{
 106		uuid_vec_t::const_iterator it = mAdded.begin(), end = mAdded.end();
 107		for(; it != end; ++it)
 108		{
 109			LLInventoryItem* item = gInventory.getItem(*it);
 110			if (!item || item->getType() != LLAssetType::AT_LANDMARK)
 111				continue;
 112
 113			// Start loading the landmark.
 114			LLLandmark* lm = gLandmarkList.getAsset(
 115					item->getAssetUUID(),
 116					boost::bind(&LLLocationInputCtrl::onLandmarkLoaded, mInput, _1));
 117			if (lm)
 118			{
 119				// Already loaded? Great, handle it immediately (the callback won't be called).
 120				mInput->onLandmarkLoaded(lm);
 121			}
 122		}
 123
 124		mAdded.clear();
 125	}
 126
 127	LLLocationInputCtrl* mInput;
 128};
 129
 130/**
 131 * Updates the "Add landmark" button once a landmark gets removed.
 132 */
 133class LLRemoveLandmarkObserver : public LLInventoryObserver
 134{
 135public:
 136	LLRemoveLandmarkObserver(LLLocationInputCtrl* input) : mInput(input) {}
 137
 138private:
 139	/*virtual*/ void changed(U32 mask)
 140	{
 141		if (mask & (~(LLInventoryObserver::LABEL|LLInventoryObserver::INTERNAL|LLInventoryObserver::ADD)))
 142		{
 143			mInput->updateAddLandmarkButton();
 144		}
 145	}
 146
 147	LLLocationInputCtrl* mInput;
 148};
 149
 150class LLParcelChangeObserver : public LLParcelObserver
 151{
 152public:
 153	LLParcelChangeObserver(LLLocationInputCtrl* input) : mInput(input) {}
 154
 155private:
 156	/*virtual*/ void changed()
 157	{
 158		if (mInput)
 159		{
 160			mInput->refreshParcelIcons();
 161		}
 162	}
 163
 164	LLLocationInputCtrl* mInput;
 165};
 166
 167//============================================================================
 168
 169
 170static LLDefaultChildRegistry::Register<LLLocationInputCtrl> r("location_input");
 171
 172LLLocationInputCtrl::Params::Params()
 173:	icon_maturity_general("icon_maturity_general"),
 174	icon_maturity_adult("icon_maturity_adult"),
 175	icon_maturity_moderate("icon_maturity_moderate"),
 176	add_landmark_image_enabled("add_landmark_image_enabled"),
 177	add_landmark_image_disabled("add_landmark_image_disabled"),
 178	add_landmark_image_hover("add_landmark_image_hover"),
 179	add_landmark_image_selected("add_landmark_image_selected"),
 180	add_landmark_hpad("add_landmark_hpad", 0),
 181	icon_hpad("icon_hpad", 0),
 182	add_landmark_button("add_landmark_button"),
 183	for_sale_button("for_sale_button"),
 184	info_button("info_button"),
 185	maturity_button("maturity_button"),
 186	voice_icon("voice_icon"),
 187	fly_icon("fly_icon"),
 188	push_icon("push_icon"),
 189	build_icon("build_icon"),
 190	scripts_icon("scripts_icon"),
 191	damage_icon("damage_icon"),
 192	damage_text("damage_text"),
 193	see_avatars_icon("see_avatars_icon"),
 194	maturity_help_topic("maturity_help_topic")
 195{
 196}
 197
 198LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)
 199:	LLComboBox(p),
 200	mIconHPad(p.icon_hpad),
 201	mAddLandmarkHPad(p.add_landmark_hpad),
 202	mLocationContextMenu(NULL),
 203	mAddLandmarkBtn(NULL),
 204	mForSaleBtn(NULL),
 205	mInfoBtn(NULL),
 206	mLandmarkImageOn(NULL),
 207	mLandmarkImageOff(NULL),
 208	mIconMaturityGeneral(NULL),
 209	mIconMaturityAdult(NULL),
 210	mIconMaturityModerate(NULL),
 211	mMaturityHelpTopic(p.maturity_help_topic)
 212{
 213	// Lets replace default LLLineEditor with LLLocationLineEditor
 214	// to make needed escaping while copying and cutting url
 215	delete mTextEntry;
 216
 217	// Can't access old mTextEntry fields as they are protected, so lets build new params
 218	// That is C&P from LLComboBox::createLineEditor function
 219	static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
 220	S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
 221	LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
 222	text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
 223
 224	LLLineEditor::Params params = p.combo_editor;
 225	params.rect(text_entry_rect);
 226	params.default_text(LLStringUtil::null);
 227	params.max_length.bytes(p.max_chars);
 228	params.keystroke_callback(boost::bind(&LLLocationInputCtrl::onTextEntry, this, _1));
 229	params.commit_on_focus_lost(false);
 230	params.follows.flags(FOLLOWS_ALL);
 231	mTextEntry = LLUICtrlFactory::create<LLURLLineEditor>(params);
 232	mTextEntry->setContextMenu(NULL);
 233	addChild(mTextEntry);
 234	// LLLineEditor is replaced with LLLocationLineEditor
 235
 236	// "Place information" button.
 237	LLButton::Params info_params = p.info_button;
 238	mInfoBtn = LLUICtrlFactory::create<LLButton>(info_params);
 239	mInfoBtn->setClickedCallback(boost::bind(&LLLocationInputCtrl::onInfoButtonClicked, this));
 240	addChild(mInfoBtn);
 241
 242	// "Add landmark" button.
 243	LLButton::Params al_params = p.add_landmark_button;
 244
 245	// Image for unselected state will be set in updateAddLandmarkButton(),
 246	// it will be either mLandmarkOn or mLandmarkOff
 247	if (p.add_landmark_image_enabled())
 248	{
 249		mLandmarkImageOn = p.add_landmark_image_enabled;
 250	}
 251	if (p.add_landmark_image_disabled())
 252	{
 253		mLandmarkImageOff = p.add_landmark_image_disabled;
 254	}
 255
 256	if(p.add_landmark_image_selected)
 257	{
 258		al_params.image_selected = p.add_landmark_image_selected;
 259	}
 260	if (p.add_landmark_image_hover())
 261	{
 262		al_params.image_hover_unselected = p.add_landmark_image_hover;
 263	}
 264
 265	al_params.click_callback.function(boost::bind(&LLLocationInputCtrl::onAddLandmarkButtonClicked, this));
 266	mAddLandmarkBtn = LLUICtrlFactory::create<LLButton>(al_params);
 267	enableAddLandmarkButton(true);
 268	addChild(mAddLandmarkBtn);
 269
 270	if (p.icon_maturity_general())
 271	{
 272		mIconMaturityGeneral = p.icon_maturity_general;
 273	}
 274	if (p.icon_maturity_adult())
 275	{
 276		mIconMaturityAdult = p.icon_maturity_adult;
 277	}
 278	if(p.icon_maturity_moderate())
 279	{
 280		mIconMaturityModerate = p.icon_maturity_moderate;
 281	}
 282
 283	LLButton::Params maturity_button = p.maturity_button;
 284	mMaturityButton = LLUICtrlFactory::create<LLButton>(maturity_button);
 285	addChild(mMaturityButton);
 286	mMaturityButton->setClickedCallback(boost::bind(&LLLocationInputCtrl::onMaturityButtonClicked, this));
 287
 288	LLButton::Params for_sale_button = p.for_sale_button;
 289	for_sale_button.tool_tip = LLTrans::getString("LocationCtrlForSaleTooltip");
 290	for_sale_button.click_callback.function(
 291		boost::bind(&LLLocationInputCtrl::onForSaleButtonClicked, this));
 292	mForSaleBtn = LLUICtrlFactory::create<LLButton>( for_sale_button );
 293	addChild(mForSaleBtn);
 294
 295	// Parcel property icons
 296	// Must be mouse-opaque so cursor stays as an arrow when hovering to
 297	// see tooltip.
 298	LLIconCtrl::Params voice_icon = p.voice_icon;
 299	voice_icon.tool_tip = LLTrans::getString("LocationCtrlVoiceTooltip");
 300	voice_icon.mouse_opaque = true;
 301	mParcelIcon[VOICE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(voice_icon);
 302	mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, VOICE_ICON));
 303	addChild(mParcelIcon[VOICE_ICON]);
 304
 305	LLIconCtrl::Params fly_icon = p.fly_icon;
 306	fly_icon.tool_tip = LLTrans::getString("LocationCtrlFlyTooltip");
 307	fly_icon.mouse_opaque = true;
 308	mParcelIcon[FLY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(fly_icon);
 309	mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, FLY_ICON));
 310	addChild(mParcelIcon[FLY_ICON]);
 311
 312	LLIconCtrl::Params push_icon = p.push_icon;
 313	push_icon.tool_tip = LLTrans::getString("LocationCtrlPushTooltip");
 314	push_icon.mouse_opaque = true;
 315	mParcelIcon[PUSH_ICON] = LLUICtrlFactory::create<LLIconCtrl>(push_icon);
 316	mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PUSH_ICON));
 317	addChild(mParcelIcon[PUSH_ICON]);
 318
 319	LLIconCtrl::Params build_icon = p.build_icon;
 320	build_icon.tool_tip = LLTrans::getString("LocationCtrlBuildTooltip");
 321	build_icon.mouse_opaque = true;
 322	mParcelIcon[BUILD_ICON] = LLUICtrlFactory::create<LLIconCtrl>(build_icon);
 323	mParcelIcon[BUILD_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, BUILD_ICON));
 324	addChild(mParcelIcon[BUILD_ICON]);
 325
 326	LLIconCtrl::Params scripts_icon = p.scripts_icon;
 327	scripts_icon.tool_tip = LLTrans::getString("LocationCtrlScriptsTooltip");
 328	scripts_icon.mouse_opaque = true;
 329	mParcelIcon[SCRIPTS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(scripts_icon);
 330	mParcelIcon[SCRIPTS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SCRIPTS_ICON));
 331	addChild(mParcelIcon[SCRIPTS_ICON]);
 332
 333	LLIconCtrl::Params damage_icon = p.damage_icon;
 334	damage_icon.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip");
 335	damage_icon.mouse_opaque = true;
 336	mParcelIcon[DAMAGE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(damage_icon);
 337	mParcelIcon[DAMAGE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, DAMAGE_ICON));
 338	addChild(mParcelIcon[DAMAGE_ICON]);
 339	
 340	LLTextBox::Params damage_text = p.damage_text;
 341	damage_text.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip");
 342	damage_text.mouse_opaque = true;
 343	mDamageText = LLUICtrlFactory::create<LLTextBox>(damage_text);
 344	addChild(mDamageText);
 345	
 346	LLIconCtrl::Params see_avatars_icon = p.see_avatars_icon;
 347	see_avatars_icon.tool_tip = LLTrans::getString("LocationCtrlSeeAVsTooltip");
 348	see_avatars_icon.mouse_opaque = true;
 349	mParcelIcon[SEE_AVATARS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(see_avatars_icon);
 350	mParcelIcon[SEE_AVATARS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SEE_AVATARS_ICON));
 351	addChild(mParcelIcon[SEE_AVATARS_ICON]);
 352	
 353	// Register callbacks and load the location field context menu (NB: the order matters).
 354	LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Navbar.Action", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemClicked, this, _2));
 355	LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("Navbar.EnableMenuItem", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemEnabled, this, _2));
 356		
 357	setPrearrangeCallback(boost::bind(&LLLocationInputCtrl::onLocationPrearrange, this, _2));
 358	getTextEntry()->setMouseUpCallback(boost::bind(&LLLocationInputCtrl::changeLocationPresentation, this));
 359
 360	// Load the location field context menu
 361	mLocationContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 362	if (!mLocationContextMenu)
 363	{
 364		llwarns << "Error loading navigation bar context menu" << llendl;
 365		
 366	}
 367	getTextEntry()->setRightMouseUpCallback(boost::bind(&LLLocationInputCtrl::onTextEditorRightClicked,this,_2,_3,_4));
 368	updateWidgetlayout();
 369
 370	// Connecting signal for updating location on "Show Coordinates" setting change.
 371	LLControlVariable* coordinates_control = gSavedSettings.getControl("NavBarShowCoordinates").get();
 372	if (coordinates_control)
 373	{
 374		mCoordinatesControlConnection = coordinates_control->getSignal()->connect(boost::bind(&LLLocationInputCtrl::refreshLocation, this));
 375	}
 376
 377	// Connecting signal for updating parcel icons on "Show Parcel Properties" setting change.
 378	LLControlVariable* parcel_properties_control = gSavedSettings.getControl("NavBarShowParcelProperties").get();
 379	if (parcel_properties_control)
 380	{
 381		mParcelPropertiesControlConnection = parcel_properties_control->getSignal()->connect(boost::bind(&LLLocationInputCtrl::refreshParcelIcons, this));
 382	}
 383
 384	// - Make the "Add landmark" button updated when either current parcel gets changed
 385	//   or a landmark gets created or removed from the inventory.
 386	// - Update the location string on parcel change.
 387	mParcelMgrConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(
 388		boost::bind(&LLLocationInputCtrl::onAgentParcelChange, this));
 389	// LLLocationHistory instance is being created before the location input control, so we have to update initial state of button manually.
 390	mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0);
 391	mLocationHistoryConnection = LLLocationHistory::getInstance()->setChangedCallback(
 392			boost::bind(&LLLocationInputCtrl::onLocationHistoryChanged, this,_1));
 393
 394	mRemoveLandmarkObserver	= new LLRemoveLandmarkObserver(this);
 395	mAddLandmarkObserver	= new LLAddLandmarkObserver(this);
 396	gInventory.addObserver(mRemoveLandmarkObserver);
 397	gInventory.addObserver(mAddLandmarkObserver);
 398
 399	mParcelChangeObserver = new LLParcelChangeObserver(this);
 400	LLViewerParcelMgr::getInstance()->addObserver(mParcelChangeObserver);
 401
 402	mAddLandmarkTooltip = LLTrans::getString("LocationCtrlAddLandmarkTooltip");
 403	mEditLandmarkTooltip = LLTrans::getString("LocationCtrlEditLandmarkTooltip");
 404	mButton->setToolTip(LLTrans::getString("LocationCtrlComboBtnTooltip"));
 405	mInfoBtn->setToolTip(LLTrans::getString("LocationCtrlInfoBtnTooltip"));
 406}
 407
 408LLLocationInputCtrl::~LLLocationInputCtrl()
 409{
 410	gInventory.removeObserver(mRemoveLandmarkObserver);
 411	gInventory.removeObserver(mAddLandmarkObserver);
 412	delete mRemoveLandmarkObserver;
 413	delete mAddLandmarkObserver;
 414
 415	LLViewerParcelMgr::getInstance()->removeObserver(mParcelChangeObserver);
 416	delete mParcelChangeObserver;
 417
 418	mCoordinatesControlConnection.disconnect();
 419	mParcelPropertiesControlConnection.disconnect();
 420	mParcelMgrConnection.disconnect();
 421	mLocationHistoryConnection.disconnect();
 422}
 423
 424void LLLocationInputCtrl::setEnabled(BOOL enabled)
 425{
 426	LLComboBox::setEnabled(enabled);
 427	mAddLandmarkBtn->setEnabled(enabled);
 428}
 429
 430void LLLocationInputCtrl::hideList()
 431{
 432	LLComboBox::hideList();
 433	if (mTextEntry && hasFocus())
 434		focusTextEntry();
 435}
 436
 437BOOL LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask)
 438{
 439
 440	if(mAddLandmarkBtn->parentPointInView(x,y))
 441	{
 442		updateAddLandmarkTooltip();
 443	}
 444	// Let the buttons show their tooltips.
 445	if (LLUICtrl::handleToolTip(x, y, mask))
 446	{
 447		if (mList->getRect().pointInRect(x, y)) 
 448		{
 449			S32 loc_x, loc_y;
 450			//x,y - contain coordinates related to the location input control, but without taking the expanded list into account
 451			//So we have to convert it again into local coordinates of mList
 452			localPointToOtherView(x,y,&loc_x,&loc_y,mList);
 453			
 454			LLScrollListItem* item =  mList->hitItem(loc_x,loc_y);
 455			if (item)
 456			{
 457				LLSD value = item->getValue();
 458				if (value.has("tooltip"))
 459				{
 460					LLToolTipMgr::instance().show(value["tooltip"]);
 461				}
 462			}
 463		}
 464
 465		return TRUE;
 466	}
 467
 468	return FALSE;
 469}
 470
 471BOOL LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask)
 472{
 473	BOOL result = LLComboBox::handleKeyHere(key, mask);
 474
 475	if (key == KEY_DOWN && hasFocus() && mList->getItemCount() != 0 && !mList->getVisible())
 476	{
 477		showList();
 478	}
 479
 480	return result;
 481}
 482
 483void LLLocationInputCtrl::onTextEntry(LLLineEditor* line_editor)
 484{
 485	KEY key = gKeyboard->currentKey();
 486	MASK mask = gKeyboard->currentMask(TRUE);
 487
 488	// Typing? (moving cursor should not affect showing the list)
 489	bool typing = mask != MASK_CONTROL && key != KEY_LEFT && key != KEY_RIGHT && key != KEY_HOME && key != KEY_END;
 490	bool pasting = mask == MASK_CONTROL && key == 'V';
 491
 492	if (line_editor->getText().empty())
 493	{
 494		prearrangeList(); // resets filter
 495		hideList();
 496	}
 497	else if (typing || pasting)
 498	{
 499		prearrangeList(line_editor->getText());
 500		if (mList->getItemCount() != 0)
 501		{
 502			showList();
 503			focusTextEntry();
 504		}
 505		else
 506		{
 507			// Hide the list if it's empty.
 508			hideList();
 509		}
 510	}
 511	
 512	LLComboBox::onTextEntry(line_editor);
 513}
 514
 515/**
 516 * Useful if we want to just set the text entry value, no matter what the list contains.
 517 *
 518 * This is faster than setTextEntry().
 519 */
 520void LLLocationInputCtrl::setText(const LLStringExplicit& text)
 521{
 522	if (mTextEntry)
 523	{
 524		mTextEntry->setText(text);
 525	}
 526	mHasAutocompletedText = FALSE;
 527}
 528
 529void LLLocationInputCtrl::setFocus(BOOL b)
 530{
 531	LLComboBox::setFocus(b);
 532
 533	if (mTextEntry && b && !mList->getVisible())
 534	{
 535		mTextEntry->setFocus(TRUE);
 536	}
 537}
 538
 539void LLLocationInputCtrl::handleLoginComplete()
 540{
 541	// An agent parcel update hasn't occurred yet, so we have to
 542	// manually set location and the appropriate "Add landmark" icon.
 543	refresh();
 544}
 545
 546//== private methods =========================================================
 547
 548void LLLocationInputCtrl::onFocusReceived()
 549{
 550	prearrangeList();
 551}
 552
 553void LLLocationInputCtrl::onFocusLost()
 554{
 555	LLUICtrl::onFocusLost();
 556	refreshLocation();
 557
 558	// Setting cursor to 0  to show the left edge of the text. See STORM-370.
 559	mTextEntry->setCursor(0);
 560
 561	if(mTextEntry->hasSelection()){
 562		mTextEntry->deselect();
 563	}
 564}
 565
 566void LLLocationInputCtrl::draw()
 567{
 568	static LLUICachedControl<bool> show_coords("NavBarShowCoordinates", false);
 569	if(!hasFocus() && show_coords)
 570	{
 571		refreshLocation();
 572	}
 573	
 574	static LLUICachedControl<bool> show_icons("NavBarShowParcelProperties", false);
 575	if (show_icons)
 576	{
 577		refreshHealth();
 578	}
 579	LLComboBox::draw();
 580}
 581
 582void LLLocationInputCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
 583{
 584	LLComboBox::reshape(width, height, called_from_parent);
 585
 586	// Setting cursor to 0  to show the left edge of the text. See EXT-4967.
 587	mTextEntry->setCursor(0);
 588	if (mTextEntry->hasSelection())
 589	{
 590		// Deselecting because selection position is changed together with
 591		// cursor position change.
 592		mTextEntry->deselect();
 593	}
 594
 595	if (isHumanReadableLocationVisible)
 596	{
 597		refreshMaturityButton();
 598	}
 599}
 600
 601void LLLocationInputCtrl::onInfoButtonClicked()
 602{
 603	LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "agent"));
 604}
 605
 606void LLLocationInputCtrl::onForSaleButtonClicked()
 607{
 608	handle_buy_land();
 609}
 610
 611void LLLocationInputCtrl::onAddLandmarkButtonClicked()
 612{
 613	LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos();
 614	// Landmark exists, open it for preview and edit
 615	if(landmark && landmark->getUUID().notNull())
 616	{
 617		LLSD key;
 618		key["type"] = "landmark";
 619		key["id"] = landmark->getUUID();
 620
 621		LLFloaterSidePanelContainer::showPanel("places", key);
 622	}
 623	else
 624	{
 625		LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "create_landmark"));
 626	}
 627}
 628
 629void LLLocationInputCtrl::onAgentParcelChange()
 630{
 631	refresh();
 632}
 633
 634void LLLocationInputCtrl::onMaturityButtonClicked()
 635{
 636	LLUI::sHelpImpl->showTopic(mMaturityHelpTopic);
 637}
 638
 639void LLLocationInputCtrl::onLandmarkLoaded(LLLandmark* lm)
 640{
 641	(void) lm;
 642	updateAddLandmarkButton();
 643}
 644
 645void LLLocationInputCtrl::onLocationHistoryChanged(LLLocationHistory::EChangeType event)
 646{
 647	if(event == LLLocationHistory::LOAD)
 648	{
 649		rebuildLocationHistory();
 650	}
 651	mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0);
 652}
 653
 654void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data)
 655{
 656	std::string filter = data.asString();
 657	rebuildLocationHistory(filter);
 658
 659	//Let's add landmarks to the top of the list if any
 660	if(!filter.empty() )
 661	{
 662		LLInventoryModel::item_array_t landmark_items = LLLandmarkActions::fetchLandmarksByName(filter, TRUE);
 663
 664		for(U32 i=0; i < landmark_items.size(); i++)
 665		{
 666			LLSD value;
 667			//TODO:: DO we need tooltip for Landmark??
 668			
 669			value["item_type"] = LANDMARK;
 670			value["AssetUUID"] =  landmark_items[i]->getAssetUUID(); 
 671			add(landmark_items[i]->getName(), value);
 672			
 673		}
 674	//Let's add teleport history items
 675		LLTeleportHistory* th = LLTeleportHistory::getInstance();
 676		LLTeleportHistory::slurl_list_t th_items = th->getItems();
 677
 678		std::set<std::string> new_item_titles;// duplicate control
 679		LLTeleportHistory::slurl_list_t::iterator result = std::find_if(
 680				th_items.begin(), th_items.end(), boost::bind(
 681						&LLLocationInputCtrl::findTeleportItemsByTitle, this,
 682						_1, filter));
 683
 684		while (result != th_items.end())
 685		{
 686			//mTitile format - region_name[, parcel_name]
 687			//mFullTitile format - region_name[, parcel_name] (local_x,local_y, local_z)
 688			if (new_item_titles.insert(result->mFullTitle).second)
 689			{
 690				LLSD value;
 691				value["item_type"] = TELEPORT_HISTORY;
 692				value["global_pos"] = result->mGlobalPos.getValue();
 693				std::string region_name = result->mTitle.substr(0, result->mTitle.find(','));
 694				//TODO*: add Surl to teleportitem or parse region name from title
 695				value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString();
 696				add(result->getTitle(), value); 
 697			}
 698			result = std::find_if(result + 1, th_items.end(), boost::bind(
 699									&LLLocationInputCtrl::findTeleportItemsByTitle, this,
 700									_1, filter));
 701		}
 702	}
 703	sortByName();
 704	
 705	mList->mouseOverHighlightNthItem(-1); // Clear highlight on the last selected item.
 706}
 707
 708bool LLLocationInputCtrl::findTeleportItemsByTitle(const LLTeleportHistoryItem& item, const std::string& filter)
 709{
 710	return item.mTitle.find(filter) != std::string::npos;
 711}
 712
 713void LLLocationInputCtrl::onTextEditorRightClicked(S32 x, S32 y, MASK mask)
 714{
 715	if (mLocationContextMenu)
 716	{
 717		updateContextMenu();
 718		mLocationContextMenu->buildDrawLabels();
 719		mLocationContextMenu->updateParent(LLMenuGL::sMenuContainer);
 720		hideList();
 721		setFocus(true);
 722		changeLocationPresentation();
 723		LLMenuGL::showPopup(this, mLocationContextMenu, x, y);
 724	}
 725}
 726
 727void LLLocationInputCtrl::refresh()
 728{
 729	refreshLocation();			// update location string
 730	refreshParcelIcons();
 731	updateAddLandmarkButton();	// indicate whether current parcel has been landmarked 
 732}
 733
 734void LLLocationInputCtrl::refreshLocation()
 735{
 736	// Is one of our children focused?
 737	if (LLUICtrl::hasFocus() || mButton->hasFocus() || mList->hasFocus() ||
 738	    (mTextEntry && mTextEntry->hasFocus()) ||
 739	    (mAddLandmarkBtn->hasFocus()))
 740	{
 741		llwarns << "Location input should not be refreshed when having focus" << llendl;
 742		return;
 743	}
 744
 745	// Update location field.
 746	std::string location_name;
 747	LLAgentUI::ELocationFormat format =
 748		(gSavedSettings.getBOOL("NavBarShowCoordinates")
 749			? LLAgentUI::LOCATION_FORMAT_FULL
 750			: LLAgentUI::LOCATION_FORMAT_NO_COORDS);
 751
 752	if (!LLAgentUI::buildLocationString(location_name, format)) 
 753	{
 754		location_name = "???";
 755	}
 756	// store human-readable location to compare it in changeLocationPresentation()
 757	mHumanReadableLocation = location_name;
 758	setText(location_name);
 759	isHumanReadableLocationVisible = true;
 760
 761	refreshMaturityButton();
 762}
 763
 764// returns new right edge
 765static S32 layout_widget(LLUICtrl* widget, S32 right)
 766{
 767	if (widget->getVisible())
 768	{
 769		LLRect rect = widget->getRect();
 770		rect.mLeft = right - rect.getWidth();
 771		rect.mRight = right;
 772		widget->setRect( rect );
 773		right -= rect.getWidth();
 774	}
 775	return right;
 776}
 777
 778void LLLocationInputCtrl::refreshParcelIcons()
 779{
 780	// Our "cursor" moving right to left
 781	S32 x = mAddLandmarkBtn->getRect().mLeft;
 782
 783	LLViewerParcelMgr* vpm = LLViewerParcelMgr::getInstance();
 784
 785	LLViewerRegion* agent_region = gAgent.getRegion();
 786	LLParcel* agent_parcel = vpm->getAgentParcel();
 787	if (!agent_region || !agent_parcel)
 788		return;
 789
 790	mForSaleBtn->setVisible(vpm->canAgentBuyParcel(agent_parcel, false));
 791
 792	x = layout_widget(mForSaleBtn, x);
 793
 794	if (gSavedSettings.getBOOL("NavBarShowParcelProperties"))
 795	{
 796		LLParcel* current_parcel;
 797		LLViewerRegion* selection_region = vpm->getSelectionRegion();
 798		LLParcel* selected_parcel = vpm->getParcelSelection()->getParcel();
 799
 800		// If agent is in selected parcel we use its properties because
 801		// they are updated more often by LLViewerParcelMgr than agent parcel properties.
 802		// See LLViewerParcelMgr::processParcelProperties().
 803		// This is needed to reflect parcel restrictions changes without having to leave
 804		// the parcel and then enter it again. See EXT-2987
 805		if (selected_parcel && selected_parcel->getLocalID() == agent_parcel->getLocalID()
 806				&& selection_region == agent_region)
 807		{
 808			current_parcel = selected_parcel;
 809		}
 810		else
 811		{
 812			current_parcel = agent_parcel;
 813		}
 814
 815		bool allow_voice	= vpm->allowAgentVoice(agent_region, current_parcel);
 816		bool allow_fly		= vpm->allowAgentFly(agent_region, current_parcel);
 817		bool allow_push		= vpm->allowAgentPush(agent_region, current_parcel);
 818		bool allow_build	= vpm->allowAgentBuild(current_parcel); // true when anyone is allowed to build. See EXT-4610.
 819		bool allow_scripts	= vpm->allowAgentScripts(agent_region, current_parcel);
 820		bool allow_damage	= vpm->allowAgentDamage(agent_region, current_parcel);
 821		bool see_avs        = current_parcel->getSeeAVs();
 822
 823		// Most icons are "block this ability"
 824		mParcelIcon[VOICE_ICON]->setVisible(   !allow_voice );
 825		mParcelIcon[FLY_ICON]->setVisible(     !allow_fly );
 826		mParcelIcon[PUSH_ICON]->setVisible(    !allow_push );
 827		mParcelIcon[BUILD_ICON]->setVisible(   !allow_build );
 828		mParcelIcon[SCRIPTS_ICON]->setVisible( !allow_scripts );
 829		mParcelIcon[DAMAGE_ICON]->setVisible(  allow_damage );
 830		mDamageText->setVisible(allow_damage);
 831		mParcelIcon[SEE_AVATARS_ICON]->setVisible( !see_avs );
 832
 833		// Padding goes to left of both landmark star and for sale btn
 834		x -= mAddLandmarkHPad;
 835
 836		// Slide the parcel icons rect from right to left, adjusting rectangles
 837		for (S32 i = 0; i < ICON_COUNT; ++i)
 838		{
 839			x = layout_widget(mParcelIcon[i], x);
 840			x -= mIconHPad;
 841		}
 842		x = layout_widget(mDamageText, x);
 843		x -= mIconHPad;
 844	}
 845	else
 846	{
 847		for (S32 i = 0; i < ICON_COUNT; ++i)
 848		{
 849			mParcelIcon[i]->setVisible(false);
 850		}
 851		mDamageText->setVisible(false);
 852	}
 853
 854	if (mTextEntry)
 855	{
 856		S32 left_pad, right_pad;
 857		mTextEntry->getTextPadding(&left_pad, &right_pad);
 858		right_pad = mTextEntry->getRect().mRight - x;
 859		mTextEntry->setTextPadding(left_pad, right_pad);
 860	}
 861}
 862
 863void LLLocationInputCtrl::refreshHealth()
 864{
 865	// *FIXME: Status bar owns health information, should be in agent
 866	if (gStatusBar)
 867	{
 868		static S32 last_health = -1;
 869		S32 health = gStatusBar->getHealth();
 870		if (health != last_health)
 871		{
 872			std::string text = llformat("%d%%", health);
 873			mDamageText->setText(text);
 874			last_health = health;
 875		}
 876	}
 877}
 878
 879void LLLocationInputCtrl::refreshMaturityButton()
 880{
 881	// Updating maturity rating icon.
 882	LLViewerRegion* region = gAgent.getRegion();
 883	if (!region)
 884		return;
 885
 886	bool button_visible = true;
 887	LLPointer<LLUIImage> rating_image = NULL;
 888	std::string rating_tooltip;
 889
 890	U8 sim_access = region->getSimAccess();
 891	switch(sim_access)
 892	{
 893	case SIM_ACCESS_PG:
 894		rating_image = mIconMaturityGeneral;
 895		rating_tooltip = LLTrans::getString("LocationCtrlGeneralIconTooltip");
 896		break;
 897
 898	case SIM_ACCESS_ADULT:
 899		rating_image = mIconMaturityAdult;
 900		rating_tooltip = LLTrans::getString("LocationCtrlAdultIconTooltip");
 901		break;
 902
 903	case SIM_ACCESS_MATURE:
 904		rating_image = mIconMaturityModerate;
 905		rating_tooltip = LLTrans::getString("LocationCtrlModerateIconTooltip");
 906		break;
 907
 908	default:
 909		button_visible = false;
 910		break;
 911	}
 912
 913	mMaturityButton->setVisible(button_visible);
 914	mMaturityButton->setToolTip(rating_tooltip);
 915	if(rating_image)
 916	{
 917		mMaturityButton->setImageUnselected(rating_image);
 918		mMaturityButton->setImagePressed(rating_image);
 919	}
 920	if (mMaturityButton->getVisible())
 921	{
 922		positionMaturityButton();
 923	}
 924}
 925
 926void LLLocationInputCtrl::positionMaturityButton()
 927{
 928	const LLFontGL* font = mTextEntry->getFont();
 929	if (!font)
 930		return;
 931
 932	S32 left_pad, right_pad;
 933	mTextEntry->getTextPadding(&left_pad, &right_pad);
 934
 935	// Calculate the right edge of rendered text + a whitespace.
 936	left_pad = left_pad + font->getWidth(mTextEntry->getText()) + font->getWidth(" ");
 937
 938	LLRect rect = mMaturityButton->getRect();
 939	mMaturityButton->setRect(rect.setOriginAndSize(left_pad, rect.mBottom, rect.getWidth(), rect.getHeight()));
 940
 941	// Hide icon if it text area is not width enough to display it, show otherwise.
 942	mMaturityButton->setVisible(rect.mRight < mTextEntry->getRect().getWidth() - right_pad);
 943}
 944
 945void LLLocationInputCtrl::rebuildLocationHistory(const std::string& filter)
 946{
 947	LLLocationHistory::location_list_t filtered_items;
 948	const LLLocationHistory::location_list_t* itemsp = NULL;
 949	LLLocationHistory* lh = LLLocationHistory::getInstance();
 950	
 951	if (filter.empty())
 952	{
 953		itemsp = &lh->getItems();
 954	}
 955	else
 956	{
 957		lh->getMatchingItems(filter, filtered_items);
 958		itemsp = &filtered_items;
 959	}
 960	
 961	removeall();
 962	for (LLLocationHistory::location_list_t::const_reverse_iterator it = itemsp->rbegin(); it != itemsp->rend(); it++)
 963	{
 964		LLSD value;
 965		value["tooltip"] = it->getToolTip();
 966		//location history can contain only typed locations
 967		value["item_type"] = TYPED_REGION_SLURL;
 968		value["global_pos"] = it->mGlobalPos.getValue();
 969		add(it->getLocation(), value);
 970	}
 971}
 972
 973void LLLocationInputCtrl::focusTextEntry()
 974{
 975	// We can't use "mTextEntry->setFocus(TRUE)" instead because
 976	// if the "select_on_focus" parameter is true it places the cursor
 977	// at the beginning (after selecting text), thus screwing up updateSelection().
 978	if (mTextEntry)
 979	{
 980		gFocusMgr.setKeyboardFocus(mTextEntry);
 981
 982		// Enable the text entry to handle accelerator keys (EXT-8104).
 983		LLEditMenuHandler::gEditMenuHandler = mTextEntry;
 984	}
 985}
 986
 987void LLLocationInputCtrl::enableAddLandmarkButton(bool val)
 988{
 989	// We don't want to disable the button because it should be click able at any time, 
 990	// instead switch images.
 991	LLUIImage* img = val ? mLandmarkImageOn : mLandmarkImageOff;
 992	if(img)
 993	{
 994		mAddLandmarkBtn->setImageUnselected(img);
 995	}
 996}
 997
 998// Change the "Add landmark" button image
 999// depending on whether current parcel has been landmarked.
1000void LLLocationInputCtrl::updateAddLandmarkButton()
1001{
1002	enableAddLandmarkButton(LLLandmarkActions::hasParcelLandmark());
1003}
1004void LLLocationInputCtrl::updateAddLandmarkTooltip()
1005{
1006	std::string tooltip;
1007	if(LLLandmarkActions::landmarkAlreadyExists())
1008	{
1009		tooltip = mEditLandmarkTooltip;
1010	}
1011	else
1012	{
1013		tooltip = mAddLandmarkTooltip;
1014	}
1015	mAddLandmarkBtn->setToolTip(tooltip);
1016}
1017
1018void LLLocationInputCtrl::updateContextMenu(){
1019
1020	if (mLocationContextMenu)
1021	{
1022		LLMenuItemGL* landmarkItem = mLocationContextMenu->getChild<LLMenuItemGL>("Landmark");
1023		if (!LLLandmarkActions::landmarkAlreadyExists())
1024		{
1025			landmarkItem->setLabel(LLTrans::getString("AddLandmarkNavBarMenu"));
1026		}
1027		else
1028		{
1029			landmarkItem->setLabel(LLTrans::getString("EditLandmarkNavBarMenu"));
1030		}
1031	}
1032}
1033void LLLocationInputCtrl::updateWidgetlayout()
1034{
1035	const LLRect&	rect			= getLocalRect();
1036	const LLRect&	hist_btn_rect	= mButton->getRect();
1037
1038	// Info button is set in the XUI XML location_input.xml
1039
1040	// "Add Landmark" button
1041	LLRect al_btn_rect = mAddLandmarkBtn->getRect();
1042	al_btn_rect.translate(
1043		hist_btn_rect.mLeft - mIconHPad - al_btn_rect.getWidth(),
1044		(rect.getHeight() - al_btn_rect.getHeight()) / 2);
1045	mAddLandmarkBtn->setRect(al_btn_rect);
1046}
1047
1048void LLLocationInputCtrl::changeLocationPresentation()
1049{
1050	if (!mTextEntry)
1051		return;
1052
1053	//change location presentation only if user does not select/paste anything and 
1054	//human-readable region name is being displayed
1055	std::string text = mTextEntry->getText();
1056	LLStringUtil::trim(text);
1057	if(!mTextEntry->hasSelection() && text == mHumanReadableLocation)
1058	{
1059		//needs unescaped one
1060		LLSLURL slurl;
1061		LLAgentUI::buildSLURL(slurl, false);
1062		mTextEntry->setText(LLURI::unescape(slurl.getSLURLString()));
1063		mTextEntry->selectAll();
1064
1065		mMaturityButton->setVisible(FALSE);
1066
1067		isHumanReadableLocationVisible = false;
1068	}
1069}
1070
1071void LLLocationInputCtrl::onLocationContextMenuItemClicked(const LLSD& userdata)
1072{
1073	std::string item = userdata.asString();
1074
1075	if (item == "show_coordinates")
1076	{
1077		gSavedSettings.setBOOL("NavBarShowCoordinates",!gSavedSettings.getBOOL("NavBarShowCoordinates"));
1078	}
1079	else if (item == "show_properties")
1080	{
1081		gSavedSettings.setBOOL("NavBarShowParcelProperties",
1082			!gSavedSettings.getBOOL("NavBarShowParcelProperties"));
1083	}
1084	else if (item == "landmark")
1085	{
1086		LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos();
1087		
1088		if(!landmark)
1089		{
1090			LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "create_landmark"));
1091		}
1092		else
1093		{
1094			LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "landmark").with("id",landmark->getUUID()));
1095
1096		}
1097	}
1098	else if (item == "cut")
1099	{
1100		mTextEntry->cut();
1101	}
1102	else if (item == "copy")
1103	{
1104		mTextEntry->copy();
1105	}
1106	else if (item == "paste")
1107	{
1108		mTextEntry->paste();
1109	}
1110	else if (item == "delete")
1111	{
1112		mTextEntry->deleteSelection();
1113	}
1114	else if (item == "select_all")
1115	{
1116		mTextEntry->selectAll();
1117	}
1118}
1119
1120bool LLLocationInputCtrl::onLocationContextMenuItemEnabled(const LLSD& userdata)
1121{
1122	std::string item = userdata.asString();
1123	
1124	if (item == "can_cut")
1125	{
1126		return mTextEntry->canCut();
1127	}
1128	else if (item == "can_copy")
1129	{
1130		return mTextEntry->canCopy();
1131	}
1132	else if (item == "can_paste")
1133	{
1134		return mTextEntry->canPaste();
1135	}
1136	else if (item == "can_delete")
1137	{
1138		return mTextEntry->canDeselect();
1139	}
1140	else if (item == "can_select_all")
1141	{
1142		return mTextEntry->canSelectAll() && (mTextEntry->getLength() > 0);
1143	}
1144	else if(item == "show_coordinates")
1145	{
1146		return gSavedSettings.getBOOL("NavBarShowCoordinates");
1147	}
1148
1149	return false;
1150}
1151
1152void LLLocationInputCtrl::onParcelIconClick(EParcelIcon icon)
1153{
1154	switch (icon)
1155	{
1156	case VOICE_ICON:
1157		LLNotificationsUtil::add("NoVoice");
1158		break;
1159	case FLY_ICON:
1160		LLNotificationsUtil::add("NoFly");
1161		break;
1162	case PUSH_ICON:
1163		LLNotificationsUtil::add("PushRestricted");
1164		break;
1165	case BUILD_ICON:
1166		LLNotificationsUtil::add("NoBuild");
1167		break;
1168	case SCRIPTS_ICON:
1169	{
1170		LLViewerRegion* region = gAgent.getRegion();
1171		if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
1172		{
1173			LLNotificationsUtil::add("ScriptsStopped");
1174		}
1175		else if(region && region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS)
1176		{
1177			LLNotificationsUtil::add("ScriptsNotRunning");
1178		}
1179		else
1180		{
1181			LLNotificationsUtil::add("NoOutsideScripts");
1182		}
1183		break;
1184	}
1185	case DAMAGE_ICON:
1186		LLNotificationsUtil::add("NotSafe");
1187		break;
1188	case SEE_AVATARS_ICON:
1189		LLNotificationsUtil::add("SeeAvatars");
1190		break;
1191	case ICON_COUNT:
1192		break;
1193	// no default to get compiler warning when a new icon gets added
1194	}
1195}