PageRenderTime 42ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/lltoastnotifypanel.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 602 lines | 434 code | 69 blank | 99 comment | 80 complexity | d2c143ee18a848199eb8afdedaa144d9 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lltoastnotifypanel.cpp
  3. * @brief Panel for notify toasts.
  4. *
  5. * $LicenseInfo:firstyear=2001&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 "lltoastnotifypanel.h"
  28. // project includes
  29. #include "llviewercontrol.h"
  30. // library includes
  31. #include "lldbstrings.h"
  32. #include "lllslconstants.h"
  33. #include "llnotifications.h"
  34. #include "lluiconstants.h"
  35. #include "llrect.h"
  36. #include "lltrans.h"
  37. #include "llnotificationsutil.h"
  38. #include "llviewermessage.h"
  39. #include "llimfloater.h"
  40. const S32 BOTTOM_PAD = VPAD * 3;
  41. const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding
  42. S32 BUTTON_WIDTH = 90;
  43. //static
  44. const LLFontGL* LLToastNotifyPanel::sFont = NULL;
  45. const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL;
  46. LLToastNotifyPanel::button_click_signal_t LLToastNotifyPanel::sButtonClickSignal;
  47. LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification, const LLRect& rect, bool show_images) :
  48. LLToastPanel(notification),
  49. mTextBox(NULL),
  50. mInfoPanel(NULL),
  51. mControlPanel(NULL),
  52. mNumOptions(0),
  53. mNumButtons(0),
  54. mAddedDefaultBtn(false),
  55. mCloseNotificationOnDestroy(true)
  56. {
  57. buildFromFile( "panel_notification.xml");
  58. if(rect != LLRect::null)
  59. {
  60. this->setShape(rect);
  61. }
  62. mInfoPanel = getChild<LLPanel>("info_panel");
  63. mControlPanel = getChild<LLPanel>("control_panel");
  64. BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth");
  65. // customize panel's attributes
  66. // is it intended for displaying a tip?
  67. mIsTip = notification->getType() == "notifytip";
  68. // is it a script dialog?
  69. mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup");
  70. // is it a caution?
  71. //
  72. // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the
  73. // notify xml template specifies that it is a caution
  74. // tip-style notification handle 'caution' differently -they display the tip in a different color
  75. mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH;
  76. // setup parameters
  77. // get a notification message
  78. mMessage = notification->getMessage();
  79. // init font variables
  80. if (!sFont)
  81. {
  82. sFont = LLFontGL::getFontSansSerif();
  83. sFontSmall = LLFontGL::getFontSansSerifSmall();
  84. }
  85. // initialize
  86. setFocusRoot(!mIsTip);
  87. // get a form for the notification
  88. LLNotificationFormPtr form(notification->getForm());
  89. // get number of elements
  90. mNumOptions = form->getNumElements();
  91. // customize panel's outfit
  92. // preliminary adjust panel's layout
  93. //move to the end
  94. //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form);
  95. // adjust text options according to the notification type
  96. // add a caution textbox at the top of a caution notification
  97. if (mIsCaution && !mIsTip)
  98. {
  99. mTextBox = getChild<LLTextBox>("caution_text_box");
  100. }
  101. else
  102. {
  103. mTextBox = getChild<LLTextEditor>("text_editor_box");
  104. }
  105. // *TODO: magic numbers(???) - copied from llnotify.cpp(250)
  106. const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE;
  107. mTextBox->setMaxTextLength(MAX_LENGTH);
  108. mTextBox->setVisible(TRUE);
  109. mTextBox->setPlainText(!show_images);
  110. mTextBox->setValue(notification->getMessage());
  111. // add buttons for a script notification
  112. if (mIsTip)
  113. {
  114. adjustPanelForTipNotice();
  115. }
  116. else
  117. {
  118. std::vector<index_button_pair_t> buttons;
  119. buttons.reserve(mNumOptions);
  120. S32 buttons_width = 0;
  121. // create all buttons and accumulate they total width to reshape mControlPanel
  122. for (S32 i = 0; i < mNumOptions; i++)
  123. {
  124. LLSD form_element = form->getElement(i);
  125. if (form_element["type"].asString() != "button")
  126. {
  127. // not a button.
  128. continue;
  129. }
  130. if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN)
  131. {
  132. // a textbox pretending to be a button.
  133. continue;
  134. }
  135. LLButton* new_button = createButton(form_element, TRUE);
  136. buttons_width += new_button->getRect().getWidth();
  137. S32 index = form_element["index"].asInteger();
  138. buttons.push_back(index_button_pair_t(index,new_button));
  139. }
  140. if (buttons.empty())
  141. {
  142. addDefaultButton();
  143. }
  144. else
  145. {
  146. const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel
  147. S32 button_panel_height = mControlPanel->getRect().getHeight();
  148. //try get an average h_pad to spread out buttons
  149. S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size()));
  150. if(h_pad < 2*HPAD)
  151. {
  152. /*
  153. * Probably it is a scriptdialog toast
  154. * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons.
  155. * In last case set default h_pad to avoid heaping of buttons
  156. */
  157. S32 button_per_row = button_panel_width / BUTTON_WIDTH;
  158. h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row
  159. if(h_pad < 2*HPAD) // still not enough space between buttons ?
  160. {
  161. h_pad = 2*HPAD;
  162. }
  163. }
  164. if (mIsScriptDialog)
  165. {
  166. // we are using default width for script buttons so we can determinate button_rows
  167. //to get a number of rows we divide the required width of the buttons to button_panel_width
  168. S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width);
  169. //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width;
  170. //reserve one row for the ignore_btn
  171. button_rows++;
  172. //calculate required panel height for scripdialog notification.
  173. button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD;
  174. }
  175. else
  176. {
  177. // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width
  178. //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width);
  179. S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width);
  180. //calculate required panel height
  181. button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD;
  182. }
  183. // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed
  184. adjustPanelForScriptNotice(button_panel_width, button_panel_height);
  185. updateButtonsLayout(buttons, h_pad);
  186. // save buttons for later use in disableButtons()
  187. mButtons.assign(buttons.begin(), buttons.end());
  188. }
  189. }
  190. // adjust panel's height to the text size
  191. mInfoPanel->setFollowsAll();
  192. snapToMessageHeight(mTextBox, MAX_LENGTH);
  193. if(notification->isReusable())
  194. {
  195. mButtonClickConnection = sButtonClickSignal.connect(
  196. boost::bind(&LLToastNotifyPanel::onToastPanelButtonClicked, this, _1, _2));
  197. if(notification->isRespondedTo())
  198. {
  199. // User selected an option in toast, now disable required buttons in IM window
  200. disableRespondedOptions(notification);
  201. }
  202. }
  203. }
  204. void LLToastNotifyPanel::addDefaultButton()
  205. {
  206. LLSD form_element;
  207. form_element.with("name", "OK").with("text", LLTrans::getString("ok")).with("default", true);
  208. LLButton* ok_btn = createButton(form_element, FALSE);
  209. LLRect new_btn_rect(ok_btn->getRect());
  210. new_btn_rect.setOriginAndSize(llabs(getRect().getWidth() - BUTTON_WIDTH)/ 2, BOTTOM_PAD,
  211. //auto_size for ok button makes it very small, so let's make it wider
  212. BUTTON_WIDTH, new_btn_rect.getHeight());
  213. ok_btn->setRect(new_btn_rect);
  214. addChild(ok_btn, -1);
  215. mNumButtons = 1;
  216. mAddedDefaultBtn = true;
  217. }
  218. LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_option)
  219. {
  220. InstanceAndS32* userdata = new InstanceAndS32;
  221. userdata->mSelf = this;
  222. userdata->mButtonName = is_option ? form_element["name"].asString() : "";
  223. mBtnCallbackData.push_back(userdata);
  224. LLButton::Params p;
  225. bool is_ignore_btn = form_element["index"].asInteger() == -1;
  226. const LLFontGL* font = is_ignore_btn ? sFontSmall: sFont; // for ignore button in script dialog
  227. p.name(form_element["name"].asString());
  228. p.label(form_element["text"].asString());
  229. p.font(font);
  230. p.rect.height = BTN_HEIGHT;
  231. p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata));
  232. p.rect.width = BUTTON_WIDTH;
  233. p.auto_resize = false;
  234. p.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
  235. if (mIsCaution)
  236. {
  237. p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
  238. p.image_color_disabled(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
  239. }
  240. // for the scriptdialog buttons we use fixed button size. This is a limit!
  241. if (!mIsScriptDialog && font->getWidth(form_element["text"].asString()) > BUTTON_WIDTH)
  242. {
  243. p.rect.width = 1;
  244. p.auto_resize = true;
  245. }
  246. else if (mIsScriptDialog && is_ignore_btn)
  247. {
  248. // this is ignore button, make it smaller
  249. p.rect.height = BTN_HEIGHT_SMALL;
  250. p.rect.width = 1;
  251. p.auto_resize = true;
  252. }
  253. LLButton* btn = LLUICtrlFactory::create<LLButton>(p);
  254. mNumButtons++;
  255. btn->autoResize();
  256. if (form_element["default"].asBoolean())
  257. {
  258. setDefaultBtn(btn);
  259. }
  260. return btn;
  261. }
  262. LLToastNotifyPanel::~LLToastNotifyPanel()
  263. {
  264. mButtonClickConnection.disconnect();
  265. std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer());
  266. if (mCloseNotificationOnDestroy && LLNotificationsUtil::find(mNotification->getID()) != NULL)
  267. {
  268. // let reusable notification be deleted
  269. mNotification->setReusable(false);
  270. if (!mNotification->isPersistent())
  271. {
  272. LLNotifications::getInstance()->cancel(mNotification);
  273. }
  274. }
  275. }
  276. void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair_t>& buttons, S32 h_pad)
  277. {
  278. S32 left = 0;
  279. //reserve place for ignore button
  280. S32 bottom_offset = mIsScriptDialog ? (BTN_HEIGHT + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD) : BOTTOM_PAD;
  281. S32 max_width = mControlPanel->getRect().getWidth();
  282. LLButton* ignore_btn = NULL;
  283. LLButton* mute_btn = NULL;
  284. for (std::vector<index_button_pair_t>::const_iterator it = buttons.begin(); it != buttons.end(); it++)
  285. {
  286. if (-2 == it->first)
  287. {
  288. mute_btn = it->second;
  289. continue;
  290. }
  291. if (it->first == -1)
  292. {
  293. ignore_btn = it->second;
  294. continue;
  295. }
  296. LLButton* btn = it->second;
  297. LLRect btn_rect(btn->getRect());
  298. if (left + btn_rect.getWidth() > max_width)// whether there is still some place for button+h_pad in the mControlPanel
  299. {
  300. // looks like we need to add button to the next row
  301. left = 0;
  302. bottom_offset += (BTN_HEIGHT + VPAD);
  303. }
  304. //we arrange buttons from bottom to top for backward support of old script
  305. btn_rect.setOriginAndSize(left, bottom_offset, btn_rect.getWidth(), btn_rect.getHeight());
  306. btn->setRect(btn_rect);
  307. left = btn_rect.mLeft + btn_rect.getWidth() + h_pad;
  308. mControlPanel->addChild(btn, -1);
  309. }
  310. U32 ignore_btn_width = 0;
  311. if (mIsScriptDialog && ignore_btn != NULL)
  312. {
  313. LLRect ignore_btn_rect(ignore_btn->getRect());
  314. S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH
  315. S32 ignore_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - ignore_btn_rect.getWidth();
  316. if (ignore_btn_left + ignore_btn_rect.getWidth() > max_width)// make sure that the ignore button is in panel
  317. {
  318. ignore_btn_left = max_width - ignore_btn_rect.getWidth() - 2 * HPAD;
  319. }
  320. ignore_btn_rect.setOriginAndSize(ignore_btn_left, BOTTOM_PAD,// always move ignore button at the bottom
  321. ignore_btn_rect.getWidth(), ignore_btn_rect.getHeight());
  322. ignore_btn->setRect(ignore_btn_rect);
  323. ignore_btn_width = ignore_btn_rect.getWidth();
  324. mControlPanel->addChild(ignore_btn, -1);
  325. }
  326. if (mIsScriptDialog && mute_btn != NULL)
  327. {
  328. LLRect mute_btn_rect(mute_btn->getRect());
  329. S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH
  330. // Place mute (Block) button to the left of the ignore button.
  331. S32 mute_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - mute_btn_rect.getWidth() - ignore_btn_width - (h_pad / 2);
  332. if (mute_btn_left + mute_btn_rect.getWidth() > max_width) // make sure that the mute button is in panel
  333. {
  334. mute_btn_left = max_width - mute_btn_rect.getWidth() - 2 * HPAD;
  335. }
  336. mute_btn_rect.setOriginAndSize(mute_btn_left, BOTTOM_PAD,// always move mute button at the bottom
  337. mute_btn_rect.getWidth(), mute_btn_rect.getHeight());
  338. mute_btn->setRect(mute_btn_rect);
  339. mControlPanel->addChild(mute_btn);
  340. }
  341. }
  342. void LLToastNotifyPanel::adjustPanelForScriptNotice(S32 button_panel_width, S32 button_panel_height)
  343. {
  344. //adjust layout
  345. // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed
  346. reshape(getRect().getWidth(), mInfoPanel->getRect().getHeight() + button_panel_height + VPAD);
  347. mControlPanel->reshape( button_panel_width, button_panel_height);
  348. }
  349. void LLToastNotifyPanel::adjustPanelForTipNotice()
  350. {
  351. LLRect info_rect = mInfoPanel->getRect();
  352. LLRect this_rect = getRect();
  353. //we don't need display ControlPanel for tips because they doesn't contain any buttons.
  354. mControlPanel->setVisible(FALSE);
  355. reshape(getRect().getWidth(), mInfoPanel->getRect().getHeight());
  356. if (mNotification->getPayload().has("respond_on_mousedown")
  357. && mNotification->getPayload()["respond_on_mousedown"] )
  358. {
  359. mInfoPanel->setMouseDownCallback(
  360. boost::bind(&LLNotification::respond,
  361. mNotification,
  362. mNotification->getResponseTemplate()));
  363. }
  364. }
  365. typedef std::set<std::string> button_name_set_t;
  366. typedef std::map<std::string, button_name_set_t> disable_button_map_t;
  367. disable_button_map_t initUserGiveItemDisableButtonMap()
  368. {
  369. // see EXT-5905 for disable rules
  370. disable_button_map_t disable_map;
  371. button_name_set_t buttons;
  372. buttons.insert("Show");
  373. disable_map.insert(std::make_pair("Show", buttons));
  374. buttons.insert("Discard");
  375. disable_map.insert(std::make_pair("Discard", buttons));
  376. buttons.insert("Mute");
  377. disable_map.insert(std::make_pair("Mute", buttons));
  378. return disable_map;
  379. }
  380. disable_button_map_t initTeleportOfferedDisableButtonMap()
  381. {
  382. disable_button_map_t disable_map;
  383. button_name_set_t buttons;
  384. buttons.insert("Teleport");
  385. buttons.insert("Cancel");
  386. disable_map.insert(std::make_pair("Teleport", buttons));
  387. disable_map.insert(std::make_pair("Cancel", buttons));
  388. return disable_map;
  389. }
  390. disable_button_map_t initFriendshipOfferedDisableButtonMap()
  391. {
  392. disable_button_map_t disable_map;
  393. button_name_set_t buttons;
  394. buttons.insert("Accept");
  395. buttons.insert("Decline");
  396. disable_map.insert(std::make_pair("Accept", buttons));
  397. disable_map.insert(std::make_pair("Decline", buttons));
  398. return disable_map;
  399. }
  400. button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name)
  401. {
  402. static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap();
  403. static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap();
  404. static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap();
  405. disable_button_map_t::const_iterator it;
  406. disable_button_map_t::const_iterator it_end;
  407. disable_button_map_t search_map;
  408. if("UserGiveItem" == notification_name)
  409. {
  410. search_map = user_give_item_disable_map;
  411. }
  412. else if("TeleportOffered" == notification_name)
  413. {
  414. search_map = teleport_offered_disable_map;
  415. }
  416. else if("OfferFriendship" == notification_name)
  417. {
  418. search_map = friendship_offered_disable_map;
  419. }
  420. it = search_map.find(button_name);
  421. it_end = search_map.end();
  422. if(it_end != it)
  423. {
  424. return it->second;
  425. }
  426. return button_name_set_t();
  427. }
  428. void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button)
  429. {
  430. button_name_set_t buttons = getButtonDisableList(notification_name, selected_button);
  431. std::vector<index_button_pair_t>::const_iterator it = mButtons.begin();
  432. for ( ; it != mButtons.end(); it++)
  433. {
  434. LLButton* btn = it->second;
  435. if(buttons.find(btn->getName()) != buttons.end())
  436. {
  437. btn->setEnabled(FALSE);
  438. }
  439. }
  440. }
  441. // static
  442. void LLToastNotifyPanel::onClickButton(void* data)
  443. {
  444. InstanceAndS32* self_and_button = (InstanceAndS32*)data;
  445. LLToastNotifyPanel* self = self_and_button->mSelf;
  446. std::string button_name = self_and_button->mButtonName;
  447. LLSD response = self->mNotification->getResponseTemplate();
  448. if (!self->mAddedDefaultBtn && !button_name.empty())
  449. {
  450. response[button_name] = true;
  451. }
  452. bool is_reusable = self->mNotification->isReusable();
  453. // When we call respond(), LLOfferInfo will delete itself in inventory_offer_callback(),
  454. // lets copy it while it's still valid.
  455. LLOfferInfo* old_info = static_cast<LLOfferInfo*>(self->mNotification->getResponder());
  456. LLOfferInfo* new_info = NULL;
  457. if(is_reusable && old_info)
  458. {
  459. new_info = new LLOfferInfo(*old_info);
  460. self->mNotification->setResponder(new_info);
  461. }
  462. self->mNotification->respond(response);
  463. if(is_reusable)
  464. {
  465. sButtonClickSignal(self->mNotification->getID(), button_name);
  466. }
  467. else
  468. {
  469. // disable all buttons
  470. self->mControlPanel->setEnabled(FALSE);
  471. }
  472. }
  473. void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name)
  474. {
  475. if(mNotification->getID() == notification_id)
  476. {
  477. disableButtons(mNotification->getName(), btn_name);
  478. }
  479. }
  480. void LLToastNotifyPanel::disableRespondedOptions(LLNotificationPtr& notification)
  481. {
  482. LLSD response = notification->getResponse();
  483. for (LLSD::map_const_iterator response_it = response.beginMap();
  484. response_it != response.endMap(); ++response_it)
  485. {
  486. if (response_it->second.isBoolean() && response_it->second.asBoolean())
  487. {
  488. // that after multiple responses there can be many pressed buttons
  489. // need to process them all
  490. disableButtons(notification->getName(), response_it->first);
  491. }
  492. }
  493. }
  494. //////////////////////////////////////////////////////////////////////////
  495. LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */,
  496. bool show_images /* = true */)
  497. : mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images)
  498. {
  499. mTextBox->setFollowsAll();
  500. }
  501. LLIMToastNotifyPanel::~LLIMToastNotifyPanel()
  502. {
  503. // We shouldn't delete notification when IM floater exists
  504. // since that notification will be reused by IM floater.
  505. // This may happened when IM floater reloads messages, exactly when user
  506. // changes layout of IM chat log(disable/enable plaintext mode).
  507. // See EXT-6500
  508. LLIMFloater* im_floater = LLIMFloater::findInstance(mSessionID);
  509. if (im_floater != NULL && !im_floater->isDead())
  510. {
  511. mCloseNotificationOnDestroy = false;
  512. }
  513. }
  514. void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
  515. {
  516. S32 text_height = mTextBox->getTextBoundingRect().getHeight();
  517. S32 widget_height = mTextBox->getRect().getHeight();
  518. S32 delta = text_height - widget_height;
  519. LLRect rc = getRect();
  520. rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height + delta);
  521. height = rc.getHeight();
  522. width = rc.getWidth();
  523. bool is_width_changed = width != getRect().getWidth();
  524. LLToastPanel::reshape(width, height, called_from_parent);
  525. // Notification height required to display the text message depends on
  526. // the width of the text box thus if panel width is changed the text box
  527. // width is also changed then reshape() is called to adjust proper height.
  528. if (is_width_changed)
  529. {
  530. reshape(width, height, called_from_parent);
  531. }
  532. }
  533. // EOF