PageRenderTime 59ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/llscriptfloater.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 607 lines | 448 code | 82 blank | 77 comment | 64 complexity | 754552cb9a59ecc5a56f74e6c3bc9259 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llscriptfloater.cpp
  3. * @brief LLScriptFloater class definition
  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. #include "llscriptfloater.h"
  28. #include "llagentcamera.h"
  29. #include "llchannelmanager.h"
  30. #include "llchiclet.h"
  31. #include "llchicletbar.h"
  32. #include "llfloaterreg.h"
  33. #include "lllslconstants.h"
  34. #include "llnotifications.h"
  35. #include "llnotificationsutil.h"
  36. #include "llscreenchannel.h"
  37. #include "llsyswellwindow.h"
  38. #include "lltoastnotifypanel.h"
  39. #include "lltoastscripttextbox.h"
  40. #include "lltrans.h"
  41. #include "llviewerwindow.h"
  42. #include "llimfloater.h"
  43. //////////////////////////////////////////////////////////////////////////
  44. //////////////////////////////////////////////////////////////////////////
  45. //////////////////////////////////////////////////////////////////////////
  46. LLUUID notification_id_to_object_id(const LLUUID& notification_id)
  47. {
  48. LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id);
  49. if(notification)
  50. {
  51. return notification->getPayload()["object_id"].asUUID();
  52. }
  53. return LLUUID::null;
  54. }
  55. //////////////////////////////////////////////////////////////////////////
  56. //////////////////////////////////////////////////////////////////////////
  57. //////////////////////////////////////////////////////////////////////////
  58. LLScriptFloater::LLScriptFloater(const LLSD& key)
  59. : LLDockableFloater(NULL, true, key)
  60. , mScriptForm(NULL)
  61. , mSaveFloaterPosition(false)
  62. {
  63. setMouseDownCallback(boost::bind(&LLScriptFloater::onMouseDown, this));
  64. setOverlapsScreenChannel(true);
  65. mIsDockedStateForcedCallback = boost::bind(&LLAgentCamera::cameraMouselook, &gAgentCamera);
  66. }
  67. bool LLScriptFloater::toggle(const LLUUID& notification_id)
  68. {
  69. LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", notification_id);
  70. // show existing floater
  71. if(floater)
  72. {
  73. if(floater->getVisible())
  74. {
  75. floater->setVisible(false);
  76. return false;
  77. }
  78. else
  79. {
  80. floater->setVisible(TRUE);
  81. floater->setFocus(FALSE);
  82. }
  83. }
  84. // create and show new floater
  85. else
  86. {
  87. show(notification_id);
  88. }
  89. LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(notification_id, true);
  90. return true;
  91. }
  92. LLScriptFloater* LLScriptFloater::show(const LLUUID& notification_id)
  93. {
  94. LLScriptFloater* floater = LLFloaterReg::getTypedInstance<LLScriptFloater>("script_floater", notification_id);
  95. floater->setNotificationId(notification_id);
  96. floater->createForm(notification_id);
  97. //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445)
  98. floater->setAutoFocus(FALSE);
  99. if(LLScriptFloaterManager::OBJ_SCRIPT == LLScriptFloaterManager::getObjectType(notification_id))
  100. {
  101. floater->setSavePosition(true);
  102. floater->restorePosition();
  103. }
  104. else
  105. {
  106. floater->dockToChiclet(true);
  107. }
  108. //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445)
  109. LLFloaterReg::showTypedInstance<LLScriptFloater>("script_floater", notification_id, FALSE);
  110. return floater;
  111. }
  112. void LLScriptFloater::setNotificationId(const LLUUID& id)
  113. {
  114. mNotificationId = id;
  115. // Lets save object id now while notification exists
  116. mObjectId = notification_id_to_object_id(id);
  117. }
  118. void LLScriptFloater::createForm(const LLUUID& notification_id)
  119. {
  120. // delete old form
  121. if(mScriptForm)
  122. {
  123. removeChild(mScriptForm);
  124. mScriptForm->die();
  125. }
  126. LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id);
  127. if(NULL == notification)
  128. {
  129. return;
  130. }
  131. // create new form
  132. LLRect toast_rect = getRect();
  133. if (isScriptTextbox(notification))
  134. {
  135. mScriptForm = new LLToastScriptTextbox(notification);
  136. }
  137. else
  138. {
  139. // LLToastNotifyPanel will fit own content in vertical direction,
  140. // but it needs an initial rect to properly calculate its width
  141. // Use an initial rect of the script floater to make the floater
  142. // window more configurable.
  143. mScriptForm = new LLToastNotifyPanel(notification, toast_rect);
  144. }
  145. addChild(mScriptForm);
  146. // position form on floater
  147. mScriptForm->setOrigin(0, 0);
  148. // make floater size fit form size
  149. LLRect panel_rect = mScriptForm->getRect();
  150. toast_rect.setLeftTopAndSize(toast_rect.mLeft, toast_rect.mTop, panel_rect.getWidth(), panel_rect.getHeight() + getHeaderHeight());
  151. setShape(toast_rect);
  152. }
  153. void LLScriptFloater::onClose(bool app_quitting)
  154. {
  155. savePosition();
  156. if(getNotificationId().notNull())
  157. {
  158. // we shouldn't kill notification on exit since it may be used as persistent.
  159. if (app_quitting)
  160. {
  161. LLScriptFloaterManager::getInstance()->onRemoveNotification(getNotificationId());
  162. }
  163. else
  164. {
  165. LLScriptFloaterManager::getInstance()->removeNotification(getNotificationId());
  166. }
  167. }
  168. }
  169. void LLScriptFloater::setDocked(bool docked, bool pop_on_undock /* = true */)
  170. {
  171. LLDockableFloater::setDocked(docked, pop_on_undock);
  172. savePosition();
  173. hideToastsIfNeeded();
  174. }
  175. void LLScriptFloater::setVisible(BOOL visible)
  176. {
  177. LLDockableFloater::setVisible(visible);
  178. hideToastsIfNeeded();
  179. if(!visible)
  180. {
  181. LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(getNotificationId());
  182. if(chiclet)
  183. {
  184. chiclet->setToggleState(false);
  185. }
  186. }
  187. }
  188. void LLScriptFloater::onMouseDown()
  189. {
  190. if(getNotificationId().notNull())
  191. {
  192. // Remove new message icon
  193. LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(getNotificationId());
  194. if (chiclet == NULL)
  195. {
  196. llerror("Dock chiclet for LLScriptFloater doesn't exist", 0);
  197. }
  198. else
  199. {
  200. chiclet->setShowNewMessagesIcon(false);
  201. }
  202. }
  203. }
  204. void LLScriptFloater::savePosition()
  205. {
  206. if(getSavePosition() && mObjectId.notNull())
  207. {
  208. LLScriptFloaterManager::FloaterPositionInfo fpi = {getRect(), isDocked()};
  209. LLScriptFloaterManager::getInstance()->saveFloaterPosition(mObjectId, fpi);
  210. }
  211. }
  212. void LLScriptFloater::restorePosition()
  213. {
  214. LLScriptFloaterManager::FloaterPositionInfo fpi;
  215. if(LLScriptFloaterManager::getInstance()->getFloaterPosition(mObjectId, fpi))
  216. {
  217. dockToChiclet(fpi.mDockState);
  218. if(!fpi.mDockState)
  219. {
  220. // Un-docked floater is opened in 0,0, now move it to saved position
  221. translate(fpi.mRect.mLeft - getRect().mLeft, fpi.mRect.mTop - getRect().mTop);
  222. }
  223. }
  224. else
  225. {
  226. dockToChiclet(true);
  227. }
  228. }
  229. void LLScriptFloater::onFocusLost()
  230. {
  231. if(getNotificationId().notNull())
  232. {
  233. LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(getNotificationId(), false);
  234. }
  235. }
  236. void LLScriptFloater::onFocusReceived()
  237. {
  238. // first focus will be received before setObjectId() call - don't toggle chiclet
  239. if(getNotificationId().notNull())
  240. {
  241. LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(getNotificationId(), true);
  242. }
  243. }
  244. void LLScriptFloater::dockToChiclet(bool dock)
  245. {
  246. if (getDockControl() == NULL)
  247. {
  248. LLChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLChiclet>(getNotificationId());
  249. if (chiclet == NULL)
  250. {
  251. llwarns << "Dock chiclet for LLScriptFloater doesn't exist" << llendl;
  252. return;
  253. }
  254. else
  255. {
  256. LLChicletBar::getInstance()->getChicletPanel()->scrollToChiclet(chiclet);
  257. }
  258. // Stop saving position while we dock floater
  259. bool save = getSavePosition();
  260. setSavePosition(false);
  261. setDockControl(new LLDockControl(chiclet, this, getDockTongue(),
  262. LLDockControl::BOTTOM));
  263. setDocked(dock);
  264. // Restore saving
  265. setSavePosition(save);
  266. }
  267. }
  268. void LLScriptFloater::hideToastsIfNeeded()
  269. {
  270. using namespace LLNotificationsUI;
  271. // find channel
  272. LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->findChannelByID(
  273. LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
  274. // update notification channel state
  275. if(channel)
  276. {
  277. channel->updateShowToastsState();
  278. channel->redrawToasts();
  279. }
  280. }
  281. //////////////////////////////////////////////////////////////////////////
  282. //////////////////////////////////////////////////////////////////////////
  283. //////////////////////////////////////////////////////////////////////////
  284. void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)
  285. {
  286. if(notification_id.isNull())
  287. {
  288. llwarns << "Invalid notification ID" << llendl;
  289. return;
  290. }
  291. // get scripted Object's ID
  292. LLUUID object_id = notification_id_to_object_id(notification_id);
  293. // Need to indicate of "new message" for object chiclets according to requirements
  294. // specified in the Message Bar design specification. See EXT-3142.
  295. bool set_new_message = false;
  296. EObjectType obj_type = getObjectType(notification_id);
  297. // LLDialog can spawn only one instance, LLLoadURL and LLGiveInventory can spawn unlimited number of instances
  298. if(OBJ_SCRIPT == obj_type)
  299. {
  300. // If an Object spawns more-than-one floater, only the newest one is shown.
  301. // The previous is automatically closed.
  302. script_notification_map_t::const_iterator it = findUsingObjectId(object_id);
  303. if(it != mNotifications.end())
  304. {
  305. LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(it->first);
  306. if(chiclet)
  307. {
  308. // Pass the new_message icon state further.
  309. set_new_message = chiclet->getShowNewMessagesIcon();
  310. }
  311. LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", it->first);
  312. if(floater)
  313. {
  314. // Generate chiclet with a "new message" indicator if a docked window was opened but not in focus. See EXT-3142.
  315. set_new_message |= !floater->hasFocus();
  316. }
  317. removeNotification(it->first);
  318. }
  319. }
  320. mNotifications.insert(std::make_pair(notification_id, object_id));
  321. // Create inventory offer chiclet for offer type notifications
  322. if( OBJ_GIVE_INVENTORY == obj_type )
  323. {
  324. LLChicletBar::instance().getChicletPanel()->createChiclet<LLInvOfferChiclet>(notification_id);
  325. }
  326. else
  327. {
  328. LLChicletBar::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(notification_id);
  329. }
  330. LLIMWellWindow::getInstance()->addObjectRow(notification_id, set_new_message);
  331. LLSD data;
  332. data["notification_id"] = notification_id;
  333. data["new_message"] = set_new_message;
  334. data["unread"] = 1; // each object has got only one floater
  335. mNewObjectSignal(data);
  336. toggleScriptFloater(notification_id, set_new_message);
  337. }
  338. void LLScriptFloaterManager::removeNotification(const LLUUID& notification_id)
  339. {
  340. LLNotificationPtr notification = LLNotifications::instance().find(notification_id);
  341. if (notification != NULL && !notification->isCancelled())
  342. {
  343. LLNotificationsUtil::cancel(notification);
  344. }
  345. onRemoveNotification(notification_id);
  346. }
  347. void LLScriptFloaterManager::onRemoveNotification(const LLUUID& notification_id)
  348. {
  349. if(notification_id.isNull())
  350. {
  351. llwarns << "Invalid notification ID" << llendl;
  352. return;
  353. }
  354. // remove related chiclet
  355. if (LLChicletBar::instanceExists())
  356. {
  357. LLChicletBar::getInstance()->getChicletPanel()->removeChiclet(notification_id);
  358. }
  359. LLIMWellWindow* im_well_window = LLIMWellWindow::findInstance();
  360. if (im_well_window)
  361. {
  362. im_well_window->removeObjectRow(notification_id);
  363. }
  364. mNotifications.erase(notification_id);
  365. // close floater
  366. LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", notification_id);
  367. if(floater)
  368. {
  369. floater->savePosition();
  370. floater->setNotificationId(LLUUID::null);
  371. floater->closeFloater();
  372. }
  373. }
  374. void LLScriptFloaterManager::toggleScriptFloater(const LLUUID& notification_id, bool set_new_message)
  375. {
  376. LLSD data;
  377. data["notification_id"] = notification_id;
  378. data["new_message"] = set_new_message;
  379. mToggleFloaterSignal(data);
  380. // toggle floater
  381. LLScriptFloater::toggle(notification_id);
  382. }
  383. LLUUID LLScriptFloaterManager::findObjectId(const LLUUID& notification_id)
  384. {
  385. script_notification_map_t::const_iterator it = mNotifications.find(notification_id);
  386. if(mNotifications.end() != it)
  387. {
  388. return it->second;
  389. }
  390. return LLUUID::null;
  391. }
  392. LLUUID LLScriptFloaterManager::findNotificationId(const LLUUID& object_id)
  393. {
  394. if(object_id.notNull())
  395. {
  396. script_notification_map_t::const_iterator it = findUsingObjectId(object_id);
  397. if(mNotifications.end() != it)
  398. {
  399. return it->first;
  400. }
  401. }
  402. return LLUUID::null;
  403. }
  404. // static
  405. LLScriptFloaterManager::EObjectType LLScriptFloaterManager::getObjectType(const LLUUID& notification_id)
  406. {
  407. if(notification_id.isNull())
  408. {
  409. llwarns << "Invalid notification ID" << llendl;
  410. return OBJ_UNKNOWN;
  411. }
  412. static const object_type_map TYPE_MAP = initObjectTypeMap();
  413. LLNotificationPtr notification = LLNotificationsUtil::find(notification_id);
  414. object_type_map::const_iterator it = TYPE_MAP.find(notification->getName());
  415. if(it != TYPE_MAP.end())
  416. {
  417. return it->second;
  418. }
  419. llwarns << "Unknown object type" << llendl;
  420. return OBJ_UNKNOWN;
  421. }
  422. // static
  423. std::string LLScriptFloaterManager::getObjectName(const LLUUID& notification_id)
  424. {
  425. using namespace LLNotificationsUI;
  426. LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id);
  427. if(!notification)
  428. {
  429. llwarns << "Invalid notification" << llendl;
  430. return LLStringUtil::null;
  431. }
  432. std::string text;
  433. switch(LLScriptFloaterManager::getObjectType(notification_id))
  434. {
  435. case LLScriptFloaterManager::OBJ_SCRIPT:
  436. text = notification->getSubstitutions()["TITLE"].asString();
  437. break;
  438. case LLScriptFloaterManager::OBJ_LOAD_URL:
  439. text = notification->getSubstitutions()["OBJECTNAME"].asString();
  440. break;
  441. case LLScriptFloaterManager::OBJ_GIVE_INVENTORY:
  442. text = notification->getSubstitutions()["OBJECTFROMNAME"].asString();
  443. break;
  444. default:
  445. text = LLTrans::getString("object");
  446. break;
  447. }
  448. return text;
  449. }
  450. //static
  451. LLScriptFloaterManager::object_type_map LLScriptFloaterManager::initObjectTypeMap()
  452. {
  453. object_type_map type_map;
  454. type_map["ScriptDialog"] = OBJ_SCRIPT;
  455. type_map["ScriptDialogGroup"] = OBJ_SCRIPT;
  456. type_map["LoadWebPage"] = OBJ_LOAD_URL;
  457. type_map["ObjectGiveItem"] = OBJ_GIVE_INVENTORY;
  458. return type_map;
  459. }
  460. LLScriptFloaterManager::script_notification_map_t::const_iterator LLScriptFloaterManager::findUsingObjectId(const LLUUID& object_id)
  461. {
  462. script_notification_map_t::const_iterator it = mNotifications.begin();
  463. for(; mNotifications.end() != it; ++it)
  464. {
  465. if(object_id == it->second)
  466. {
  467. return it;
  468. }
  469. }
  470. return mNotifications.end();
  471. }
  472. void LLScriptFloaterManager::saveFloaterPosition(const LLUUID& object_id, const FloaterPositionInfo& fpi)
  473. {
  474. if(object_id.notNull())
  475. {
  476. LLScriptFloaterManager::getInstance()->mFloaterPositions[object_id] = fpi;
  477. }
  478. else
  479. {
  480. llwarns << "Invalid object id" << llendl;
  481. }
  482. }
  483. bool LLScriptFloaterManager::getFloaterPosition(const LLUUID& object_id, FloaterPositionInfo& fpi)
  484. {
  485. floater_position_map_t::const_iterator it = mFloaterPositions.find(object_id);
  486. if(LLScriptFloaterManager::getInstance()->mFloaterPositions.end() != it)
  487. {
  488. fpi = it->second;
  489. return true;
  490. }
  491. return false;
  492. }
  493. void LLScriptFloaterManager::setFloaterVisible(const LLUUID& notification_id, bool visible)
  494. {
  495. LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>(
  496. "script_floater", notification_id);
  497. if(floater)
  498. {
  499. floater->setVisible(visible);
  500. }
  501. }
  502. //////////////////////////////////////////////////////////////////
  503. bool LLScriptFloater::isScriptTextbox(LLNotificationPtr notification)
  504. {
  505. // get a form for the notification
  506. LLNotificationFormPtr form(notification->getForm());
  507. if (form)
  508. {
  509. // get number of elements in the form
  510. int num_options = form->getNumElements();
  511. // if ANY of the buttons have the magic lltextbox string as
  512. // name, then treat the whole dialog as a simple text entry
  513. // box (i.e. mixed button and textbox forms are not supported)
  514. for (int i=0; i<num_options; ++i)
  515. {
  516. LLSD form_element = form->getElement(i);
  517. if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN)
  518. {
  519. return true;
  520. }
  521. }
  522. }
  523. return false;
  524. }
  525. // EOF