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