PageRenderTime 174ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llui/llbutton.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1249 lines | 968 code | 147 blank | 134 comment | 143 complexity | 7b768b78a517fefad3f673a645257548 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llbutton.cpp
  3. * @brief LLButton base class
  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 "linden_common.h"
  27. #define LLBUTTON_CPP
  28. #include "llbutton.h"
  29. // Linden library includes
  30. #include "v4color.h"
  31. #include "llstring.h"
  32. // Project includes
  33. #include "llkeyboard.h"
  34. #include "llui.h"
  35. #include "lluiconstants.h"
  36. #include "llresmgr.h"
  37. #include "llcriticaldamp.h"
  38. #include "llfloater.h"
  39. #include "llfloaterreg.h"
  40. #include "llfocusmgr.h"
  41. #include "llwindow.h"
  42. #include "llnotificationsutil.h"
  43. #include "llrender.h"
  44. #include "lluictrlfactory.h"
  45. #include "llhelp.h"
  46. #include "lldockablefloater.h"
  47. static LLDefaultChildRegistry::Register<LLButton> r("button");
  48. // Compiler optimization, generate extern template
  49. template class LLButton* LLView::getChild<class LLButton>(
  50. const std::string& name, BOOL recurse) const;
  51. // globals loaded from settings.xml
  52. S32 LLBUTTON_H_PAD = 0;
  53. S32 BTN_HEIGHT_SMALL= 0;
  54. S32 BTN_HEIGHT = 0;
  55. LLButton::Params::Params()
  56. : label_selected("label_selected"), // requires is_toggle true
  57. label_shadow("label_shadow", true),
  58. auto_resize("auto_resize", false),
  59. use_ellipses("use_ellipses", false),
  60. image_unselected("image_unselected"),
  61. image_selected("image_selected"),
  62. image_hover_selected("image_hover_selected"),
  63. image_hover_unselected("image_hover_unselected"),
  64. image_disabled_selected("image_disabled_selected"),
  65. image_disabled("image_disabled"),
  66. image_pressed("image_pressed"),
  67. image_pressed_selected("image_pressed_selected"),
  68. image_overlay("image_overlay"),
  69. image_overlay_alignment("image_overlay_alignment", std::string("center")),
  70. image_top_pad("image_top_pad"),
  71. image_bottom_pad("image_bottom_pad"),
  72. imgoverlay_label_space("imgoverlay_label_space", 1),
  73. label_color("label_color"),
  74. label_color_selected("label_color_selected"), // requires is_toggle true
  75. label_color_disabled("label_color_disabled"),
  76. label_color_disabled_selected("label_color_disabled_selected"),
  77. image_color("image_color"),
  78. image_color_disabled("image_color_disabled"),
  79. image_overlay_color("image_overlay_color", LLColor4::white % 0.75f),
  80. image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f),
  81. image_overlay_selected_color("image_overlay_selected_color", LLColor4::white),
  82. flash_color("flash_color"),
  83. pad_right("pad_right", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
  84. pad_left("pad_left", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
  85. pad_bottom("pad_bottom"),
  86. click_callback("click_callback"),
  87. mouse_down_callback("mouse_down_callback"),
  88. mouse_up_callback("mouse_up_callback"),
  89. mouse_held_callback("mouse_held_callback"),
  90. is_toggle("is_toggle", false),
  91. scale_image("scale_image", true),
  92. hover_glow_amount("hover_glow_amount"),
  93. commit_on_return("commit_on_return", true),
  94. display_pressed_state("display_pressed_state", true),
  95. use_draw_context_alpha("use_draw_context_alpha", true),
  96. badge("badge"),
  97. handle_right_mouse("handle_right_mouse"),
  98. held_down_delay("held_down_delay"),
  99. button_flash_count("button_flash_count"),
  100. button_flash_rate("button_flash_rate")
  101. {
  102. addSynonym(is_toggle, "toggle");
  103. changeDefault(initial_value, LLSD(false));
  104. }
  105. LLButton::LLButton(const LLButton::Params& p)
  106. : LLUICtrl(p),
  107. LLBadgeOwner(getHandle()),
  108. mMouseDownFrame(0),
  109. mMouseHeldDownCount(0),
  110. mBorderEnabled( FALSE ),
  111. mFlashing( FALSE ),
  112. mCurGlowStrength(0.f),
  113. mNeedsHighlight(FALSE),
  114. mUnselectedLabel(p.label()),
  115. mSelectedLabel(p.label_selected()),
  116. mGLFont(p.font),
  117. mHeldDownDelay(p.held_down_delay.seconds), // seconds until held-down callback is called
  118. mHeldDownFrameDelay(p.held_down_delay.frames),
  119. mImageUnselected(p.image_unselected),
  120. mImageSelected(p.image_selected),
  121. mImageDisabled(p.image_disabled),
  122. mImageDisabledSelected(p.image_disabled_selected),
  123. mImageFlash(p.image_flash),
  124. mImagePressed(p.image_pressed),
  125. mImagePressedSelected(p.image_pressed_selected),
  126. mImageHoverSelected(p.image_hover_selected),
  127. mImageHoverUnselected(p.image_hover_unselected),
  128. mUnselectedLabelColor(p.label_color()),
  129. mSelectedLabelColor(p.label_color_selected()),
  130. mDisabledLabelColor(p.label_color_disabled()),
  131. mDisabledSelectedLabelColor(p.label_color_disabled_selected()),
  132. mImageColor(p.image_color()),
  133. mFlashBgColor(p.flash_color()),
  134. mDisabledImageColor(p.image_color_disabled()),
  135. mImageOverlay(p.image_overlay()),
  136. mImageOverlayColor(p.image_overlay_color()),
  137. mImageOverlayDisabledColor(p.image_overlay_disabled_color()),
  138. mImageOverlaySelectedColor(p.image_overlay_selected_color()),
  139. mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)),
  140. mImageOverlayTopPad(p.image_top_pad),
  141. mImageOverlayBottomPad(p.image_bottom_pad),
  142. mImgOverlayLabelSpace(p.imgoverlay_label_space),
  143. mIsToggle(p.is_toggle),
  144. mScaleImage(p.scale_image),
  145. mDropShadowedText(p.label_shadow),
  146. mAutoResize(p.auto_resize),
  147. mUseEllipses( p.use_ellipses ),
  148. mHAlign(p.font_halign),
  149. mLeftHPad(p.pad_left),
  150. mRightHPad(p.pad_right),
  151. mBottomVPad(p.pad_bottom),
  152. mHoverGlowStrength(p.hover_glow_amount),
  153. mCommitOnReturn(p.commit_on_return),
  154. mFadeWhenDisabled(FALSE),
  155. mForcePressedState(false),
  156. mDisplayPressedState(p.display_pressed_state),
  157. mLastDrawCharsCount(0),
  158. mMouseDownSignal(NULL),
  159. mMouseUpSignal(NULL),
  160. mHeldDownSignal(NULL),
  161. mUseDrawContextAlpha(p.use_draw_context_alpha),
  162. mHandleRightMouse(p.handle_right_mouse),
  163. mButtonFlashCount(p.button_flash_count),
  164. mButtonFlashRate(p.button_flash_rate)
  165. {
  166. static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
  167. static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
  168. if (!p.label_selected.isProvided())
  169. {
  170. mSelectedLabel = mUnselectedLabel;
  171. }
  172. // Hack to make sure there is space for at least one character
  173. if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
  174. {
  175. // Use old defaults
  176. mLeftHPad = llbutton_orig_h_pad;
  177. mRightHPad = llbutton_orig_h_pad;
  178. }
  179. mMouseDownTimer.stop();
  180. // if custom unselected button image provided...
  181. if (p.image_unselected != default_params.image_unselected)
  182. {
  183. //...fade it out for disabled image by default...
  184. if (p.image_disabled() == default_params.image_disabled() )
  185. {
  186. mImageDisabled = p.image_unselected;
  187. mFadeWhenDisabled = TRUE;
  188. }
  189. if (p.image_pressed_selected == default_params.image_pressed_selected)
  190. {
  191. mImagePressedSelected = mImageUnselected;
  192. }
  193. }
  194. // if custom selected button image provided...
  195. if (p.image_selected != default_params.image_selected)
  196. {
  197. //...fade it out for disabled image by default...
  198. if (p.image_disabled_selected() == default_params.image_disabled_selected())
  199. {
  200. mImageDisabledSelected = p.image_selected;
  201. mFadeWhenDisabled = TRUE;
  202. }
  203. if (p.image_pressed == default_params.image_pressed)
  204. {
  205. mImagePressed = mImageSelected;
  206. }
  207. }
  208. if (!p.image_pressed.isProvided())
  209. {
  210. mImagePressed = mImageSelected;
  211. }
  212. if (!p.image_pressed_selected.isProvided())
  213. {
  214. mImagePressedSelected = mImageUnselected;
  215. }
  216. if (mImageUnselected.isNull())
  217. {
  218. llwarns << "Button: " << getName() << " with no image!" << llendl;
  219. }
  220. if (p.click_callback.isProvided())
  221. {
  222. setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback
  223. }
  224. if (p.mouse_down_callback.isProvided())
  225. {
  226. setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
  227. }
  228. if (p.mouse_up_callback.isProvided())
  229. {
  230. setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
  231. }
  232. if (p.mouse_held_callback.isProvided())
  233. {
  234. setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
  235. }
  236. if (p.badge.isProvided())
  237. {
  238. LLBadgeOwner::initBadgeParams(p.badge());
  239. }
  240. }
  241. LLButton::~LLButton()
  242. {
  243. delete mMouseDownSignal;
  244. delete mMouseUpSignal;
  245. delete mHeldDownSignal;
  246. }
  247. // HACK: Committing a button is the same as instantly clicking it.
  248. // virtual
  249. void LLButton::onCommit()
  250. {
  251. // WARNING: Sometimes clicking a button destroys the floater or
  252. // panel containing it. Therefore we need to call LLUICtrl::onCommit()
  253. // LAST, otherwise this becomes deleted memory.
  254. if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
  255. if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
  256. if (getSoundFlags() & MOUSE_DOWN)
  257. {
  258. make_ui_sound("UISndClick");
  259. }
  260. if (getSoundFlags() & MOUSE_UP)
  261. {
  262. make_ui_sound("UISndClickRelease");
  263. }
  264. if (mIsToggle)
  265. {
  266. toggleState();
  267. }
  268. // do this last, as it can result in destroying this button
  269. LLUICtrl::onCommit();
  270. }
  271. boost::signals2::connection LLButton::setClickedCallback(const CommitCallbackParam& cb)
  272. {
  273. return setClickedCallback(initCommitCallback(cb));
  274. }
  275. boost::signals2::connection LLButton::setMouseDownCallback(const CommitCallbackParam& cb)
  276. {
  277. return setMouseDownCallback(initCommitCallback(cb));
  278. }
  279. boost::signals2::connection LLButton::setMouseUpCallback(const CommitCallbackParam& cb)
  280. {
  281. return setMouseUpCallback(initCommitCallback(cb));
  282. }
  283. boost::signals2::connection LLButton::setHeldDownCallback(const CommitCallbackParam& cb)
  284. {
  285. return setHeldDownCallback(initCommitCallback(cb));
  286. }
  287. boost::signals2::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb )
  288. {
  289. if (!mCommitSignal) mCommitSignal = new commit_signal_t();
  290. return mCommitSignal->connect(cb);
  291. }
  292. boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
  293. {
  294. if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
  295. return mMouseDownSignal->connect(cb);
  296. }
  297. boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
  298. {
  299. if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
  300. return mMouseUpSignal->connect(cb);
  301. }
  302. boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
  303. {
  304. if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t();
  305. return mHeldDownSignal->connect(cb);
  306. }
  307. // *TODO: Deprecate (for backwards compatibility only)
  308. boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
  309. {
  310. return setClickedCallback(boost::bind(cb, data));
  311. }
  312. boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
  313. {
  314. return setMouseDownCallback(boost::bind(cb, data));
  315. }
  316. boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
  317. {
  318. return setMouseUpCallback(boost::bind(cb, data));
  319. }
  320. boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
  321. {
  322. return setHeldDownCallback(boost::bind(cb, data));
  323. }
  324. BOOL LLButton::postBuild()
  325. {
  326. autoResize();
  327. addBadgeToParentPanel();
  328. return LLUICtrl::postBuild();
  329. }
  330. BOOL LLButton::handleUnicodeCharHere(llwchar uni_char)
  331. {
  332. BOOL handled = FALSE;
  333. if(' ' == uni_char
  334. && !gKeyboard->getKeyRepeated(' '))
  335. {
  336. if (mIsToggle)
  337. {
  338. toggleState();
  339. }
  340. LLUICtrl::onCommit();
  341. handled = TRUE;
  342. }
  343. return handled;
  344. }
  345. BOOL LLButton::handleKeyHere(KEY key, MASK mask )
  346. {
  347. BOOL handled = FALSE;
  348. if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
  349. {
  350. if (mIsToggle)
  351. {
  352. toggleState();
  353. }
  354. handled = TRUE;
  355. LLUICtrl::onCommit();
  356. }
  357. return handled;
  358. }
  359. BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
  360. {
  361. if (!childrenHandleMouseDown(x, y, mask))
  362. {
  363. // Route future Mouse messages here preemptively. (Release on mouse up.)
  364. gFocusMgr.setMouseCapture( this );
  365. if (hasTabStop() && !getIsChrome())
  366. {
  367. setFocus(TRUE);
  368. }
  369. /*
  370. * ATTENTION! This call fires another mouse down callback.
  371. * If you wish to remove this call emit that signal directly
  372. * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
  373. */
  374. LLUICtrl::handleMouseDown(x, y, mask);
  375. if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
  376. mMouseDownTimer.start();
  377. mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
  378. mMouseHeldDownCount = 0;
  379. if (getSoundFlags() & MOUSE_DOWN)
  380. {
  381. make_ui_sound("UISndClick");
  382. }
  383. }
  384. return TRUE;
  385. }
  386. BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
  387. {
  388. // We only handle the click if the click both started and ended within us
  389. if( hasMouseCapture() )
  390. {
  391. // Always release the mouse
  392. gFocusMgr.setMouseCapture( NULL );
  393. /*
  394. * ATTENTION! This call fires another mouse up callback.
  395. * If you wish to remove this call emit that signal directly
  396. * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
  397. */
  398. LLUICtrl::handleMouseUp(x, y, mask);
  399. // Regardless of where mouseup occurs, handle callback
  400. if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
  401. resetMouseDownTimer();
  402. // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
  403. // If mouseup in the widget, it's been clicked
  404. if (pointInView(x, y))
  405. {
  406. if (getSoundFlags() & MOUSE_UP)
  407. {
  408. make_ui_sound("UISndClickRelease");
  409. }
  410. if (mIsToggle)
  411. {
  412. toggleState();
  413. }
  414. LLUICtrl::onCommit();
  415. }
  416. }
  417. else
  418. {
  419. childrenHandleMouseUp(x, y, mask);
  420. }
  421. return TRUE;
  422. }
  423. BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
  424. {
  425. if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask))
  426. {
  427. // Route future Mouse messages here preemptively. (Release on mouse up.)
  428. gFocusMgr.setMouseCapture( this );
  429. if (hasTabStop() && !getIsChrome())
  430. {
  431. setFocus(TRUE);
  432. }
  433. // if (pointInView(x, y))
  434. // {
  435. // }
  436. // send the mouse down signal
  437. LLUICtrl::handleRightMouseDown(x,y,mask);
  438. // *TODO: Return result of LLUICtrl call above? Should defer to base class
  439. // but this might change the mouse handling of existing buttons in a bad way
  440. // if they are not mouse opaque.
  441. }
  442. return TRUE;
  443. }
  444. BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
  445. {
  446. if (mHandleRightMouse)
  447. {
  448. // We only handle the click if the click both started and ended within us
  449. if( hasMouseCapture() )
  450. {
  451. // Always release the mouse
  452. gFocusMgr.setMouseCapture( NULL );
  453. // if (pointInView(x, y))
  454. // {
  455. // mRightMouseUpSignal(this, x,y,mask);
  456. // }
  457. }
  458. else
  459. {
  460. childrenHandleRightMouseUp(x, y, mask);
  461. }
  462. // send the mouse up signal
  463. LLUICtrl::handleRightMouseUp(x,y,mask);
  464. // *TODO: Return result of LLUICtrl call above? Should defer to base class
  465. // but this might change the mouse handling of existing buttons in a bad way.
  466. // if they are not mouse opaque.
  467. }
  468. return TRUE;
  469. }
  470. void LLButton::onMouseLeave(S32 x, S32 y, MASK mask)
  471. {
  472. LLUICtrl::onMouseLeave(x, y, mask);
  473. mNeedsHighlight = FALSE;
  474. }
  475. void LLButton::setHighlight(bool b)
  476. {
  477. mNeedsHighlight = b;
  478. }
  479. BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
  480. {
  481. if (isInEnabledChain()
  482. && (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this))
  483. mNeedsHighlight = TRUE;
  484. if (!childrenHandleHover(x, y, mask))
  485. {
  486. if (mMouseDownTimer.getStarted())
  487. {
  488. F32 elapsed = getHeldDownTime();
  489. if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
  490. {
  491. LLSD param;
  492. param["count"] = mMouseHeldDownCount++;
  493. if (mHeldDownSignal) (*mHeldDownSignal)(this, param);
  494. }
  495. }
  496. // We only handle the click if the click both started and ended within us
  497. getWindow()->setCursor(UI_CURSOR_ARROW);
  498. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
  499. }
  500. return TRUE;
  501. }
  502. void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height)
  503. {
  504. overlay_width = mImageOverlay->getWidth();
  505. overlay_height = mImageOverlay->getHeight();
  506. F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f);
  507. overlay_width = llround((F32)overlay_width * scale_factor);
  508. overlay_height = llround((F32)overlay_height * scale_factor);
  509. }
  510. // virtual
  511. void LLButton::draw()
  512. {
  513. static LLCachedControl<bool> sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true);
  514. F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
  515. bool flash = FALSE;
  516. if( mFlashing)
  517. {
  518. if ( sEnableButtonFlashing)
  519. {
  520. F32 elapsed = mFlashingTimer.getElapsedTimeF32();
  521. S32 flash_count = S32(elapsed * mButtonFlashRate * 2.f);
  522. // flash on or off?
  523. flash = (flash_count % 2 == 0) || flash_count > S32((F32)mButtonFlashCount * 2.f);
  524. }
  525. else
  526. { // otherwise just highlight button in flash color
  527. flash = true;
  528. }
  529. }
  530. bool pressed_by_keyboard = FALSE;
  531. if (hasFocus())
  532. {
  533. pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
  534. }
  535. bool mouse_pressed_and_over = false;
  536. if (hasMouseCapture())
  537. {
  538. S32 local_mouse_x ;
  539. S32 local_mouse_y;
  540. LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
  541. mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y);
  542. }
  543. bool enabled = isInEnabledChain();
  544. bool pressed = pressed_by_keyboard
  545. || mouse_pressed_and_over
  546. || mForcePressedState;
  547. bool selected = getToggleState();
  548. bool use_glow_effect = FALSE;
  549. LLColor4 glow_color = LLColor4::white;
  550. LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
  551. LLUIImage* imagep = NULL;
  552. if (pressed && mDisplayPressedState)
  553. {
  554. imagep = selected ? mImagePressedSelected : mImagePressed;
  555. }
  556. else if ( mNeedsHighlight )
  557. {
  558. if (selected)
  559. {
  560. if (mImageHoverSelected)
  561. {
  562. imagep = mImageHoverSelected;
  563. }
  564. else
  565. {
  566. imagep = mImageSelected;
  567. use_glow_effect = TRUE;
  568. }
  569. }
  570. else
  571. {
  572. if (mImageHoverUnselected)
  573. {
  574. imagep = mImageHoverUnselected;
  575. }
  576. else
  577. {
  578. imagep = mImageUnselected;
  579. use_glow_effect = TRUE;
  580. }
  581. }
  582. }
  583. else
  584. {
  585. imagep = selected ? mImageSelected : mImageUnselected;
  586. }
  587. // Override if more data is available
  588. // HACK: Use gray checked state to mean either:
  589. // enabled and tentative
  590. // or
  591. // disabled but checked
  592. if (!mImageDisabledSelected.isNull()
  593. &&
  594. ( (enabled && getTentative())
  595. || (!enabled && selected ) ) )
  596. {
  597. imagep = mImageDisabledSelected;
  598. }
  599. else if (!mImageDisabled.isNull()
  600. && !enabled
  601. && !selected)
  602. {
  603. imagep = mImageDisabled;
  604. }
  605. if (mFlashing)
  606. {
  607. // if button should flash and we have icon for flashing, use it as image for button
  608. if(flash && mImageFlash)
  609. {
  610. // setting flash to false to avoid its further influence on glow
  611. flash = false;
  612. imagep = mImageFlash;
  613. }
  614. // else use usual flashing via flash_color
  615. else
  616. {
  617. LLColor4 flash_color = mFlashBgColor.get();
  618. use_glow_effect = TRUE;
  619. glow_type = LLRender::BT_ALPHA; // blend the glow
  620. if (mNeedsHighlight) // highlighted AND flashing
  621. glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
  622. else
  623. glow_color = flash_color;
  624. }
  625. }
  626. if (mNeedsHighlight && !imagep)
  627. {
  628. use_glow_effect = TRUE;
  629. }
  630. // Figure out appropriate color for the text
  631. LLColor4 label_color;
  632. // label changes when button state changes, not when pressed
  633. if ( enabled )
  634. {
  635. if ( getToggleState() )
  636. {
  637. label_color = mSelectedLabelColor.get();
  638. }
  639. else
  640. {
  641. label_color = mUnselectedLabelColor.get();
  642. }
  643. }
  644. else
  645. {
  646. if ( getToggleState() )
  647. {
  648. label_color = mDisabledSelectedLabelColor.get();
  649. }
  650. else
  651. {
  652. label_color = mDisabledLabelColor.get();
  653. }
  654. }
  655. // Unselected label assignments
  656. LLWString label = getCurrentLabel();
  657. // overlay with keyboard focus border
  658. if (hasFocus())
  659. {
  660. F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
  661. drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt)));
  662. }
  663. if (use_glow_effect)
  664. {
  665. mCurGlowStrength = lerp(mCurGlowStrength,
  666. mFlashing ? (flash? 1.0 : 0.0)
  667. : mHoverGlowStrength,
  668. LLCriticalDamp::getInterpolant(0.05f));
  669. }
  670. else
  671. {
  672. mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
  673. }
  674. // Draw button image, if available.
  675. // Otherwise draw basic rectangular button.
  676. if (imagep != NULL)
  677. {
  678. // apply automatic 50% alpha fade to disabled image
  679. LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get();
  680. if ( mScaleImage)
  681. {
  682. imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha );
  683. if (mCurGlowStrength > 0.01f)
  684. {
  685. gGL.setSceneBlendType(glow_type);
  686. imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
  687. gGL.setSceneBlendType(LLRender::BT_ALPHA);
  688. }
  689. }
  690. else
  691. {
  692. imagep->draw(0, 0, (enabled ? mImageColor.get() : disabled_color) % alpha );
  693. if (mCurGlowStrength > 0.01f)
  694. {
  695. gGL.setSceneBlendType(glow_type);
  696. imagep->drawSolid(0, 0, glow_color % (mCurGlowStrength * alpha));
  697. gGL.setSceneBlendType(LLRender::BT_ALPHA);
  698. }
  699. }
  700. }
  701. else
  702. {
  703. // no image
  704. lldebugs << "No image for button " << getName() << llendl;
  705. // draw it in pink so we can find it
  706. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE);
  707. }
  708. // let overlay image and text play well together
  709. S32 text_left = mLeftHPad;
  710. S32 text_right = getRect().getWidth() - mRightHPad;
  711. S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
  712. // draw overlay image
  713. if (mImageOverlay.notNull())
  714. {
  715. // get max width and height (discard level 0)
  716. S32 overlay_width;
  717. S32 overlay_height;
  718. getOverlayImageSize(overlay_width, overlay_height);
  719. S32 center_x = getLocalRect().getCenterX();
  720. S32 center_y = getLocalRect().getCenterY();
  721. //FUGLY HACK FOR "DEPRESSED" BUTTONS
  722. if (pressed && mDisplayPressedState)
  723. {
  724. center_y--;
  725. center_x++;
  726. }
  727. center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
  728. // fade out overlay images on disabled buttons
  729. LLColor4 overlay_color = mImageOverlayColor.get();
  730. if (!enabled)
  731. {
  732. overlay_color = mImageOverlayDisabledColor.get();
  733. }
  734. else if (getToggleState())
  735. {
  736. overlay_color = mImageOverlaySelectedColor.get();
  737. }
  738. overlay_color.mV[VALPHA] *= alpha;
  739. switch(mImageOverlayAlignment)
  740. {
  741. case LLFontGL::LEFT:
  742. text_left += overlay_width + mImgOverlayLabelSpace;
  743. text_width -= overlay_width + mImgOverlayLabelSpace;
  744. mImageOverlay->draw(
  745. mLeftHPad,
  746. center_y - (overlay_height / 2),
  747. overlay_width,
  748. overlay_height,
  749. overlay_color);
  750. break;
  751. case LLFontGL::HCENTER:
  752. mImageOverlay->draw(
  753. center_x - (overlay_width / 2),
  754. center_y - (overlay_height / 2),
  755. overlay_width,
  756. overlay_height,
  757. overlay_color);
  758. break;
  759. case LLFontGL::RIGHT:
  760. text_right -= overlay_width + mImgOverlayLabelSpace;
  761. text_width -= overlay_width + mImgOverlayLabelSpace;
  762. mImageOverlay->draw(
  763. getRect().getWidth() - mRightHPad - overlay_width,
  764. center_y - (overlay_height / 2),
  765. overlay_width,
  766. overlay_height,
  767. overlay_color);
  768. break;
  769. default:
  770. // draw nothing
  771. break;
  772. }
  773. }
  774. // Draw label
  775. if( !label.empty() )
  776. {
  777. LLWStringUtil::trim(label);
  778. S32 x;
  779. switch( mHAlign )
  780. {
  781. case LLFontGL::RIGHT:
  782. x = text_right;
  783. break;
  784. case LLFontGL::HCENTER:
  785. x = text_left + (text_width / 2);
  786. break;
  787. case LLFontGL::LEFT:
  788. default:
  789. x = text_left;
  790. break;
  791. }
  792. S32 y_offset = 2 + (getRect().getHeight() - 20)/2;
  793. if (pressed && mDisplayPressedState)
  794. {
  795. y_offset--;
  796. x++;
  797. }
  798. // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as
  799. // max_chars.
  800. // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value.
  801. // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode.
  802. // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars.
  803. mLastDrawCharsCount = mGLFont->render(label, 0,
  804. (F32)x,
  805. (F32)(mBottomVPad + y_offset),
  806. label_color % alpha,
  807. mHAlign, LLFontGL::BOTTOM,
  808. LLFontGL::NORMAL,
  809. mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
  810. S32_MAX, text_width,
  811. NULL, mUseEllipses);
  812. }
  813. LLUICtrl::draw();
  814. }
  815. void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size)
  816. {
  817. if (imagep == NULL) return;
  818. if (mScaleImage)
  819. {
  820. imagep->drawBorder(getLocalRect(), color, size);
  821. }
  822. else
  823. {
  824. imagep->drawBorder(0, 0, color, size);
  825. }
  826. }
  827. BOOL LLButton::getToggleState() const
  828. {
  829. return getValue().asBoolean();
  830. }
  831. void LLButton::setToggleState(BOOL b)
  832. {
  833. if( b != getToggleState() )
  834. {
  835. setControlValue(b); // will fire LLControlVariable callbacks (if any)
  836. setValue(b); // may or may not be redundant
  837. // Unselected label assignments
  838. autoResize();
  839. }
  840. }
  841. void LLButton::setFlashing( BOOL b )
  842. {
  843. if ((bool)b != mFlashing)
  844. {
  845. mFlashing = b;
  846. mFlashingTimer.reset();
  847. }
  848. }
  849. BOOL LLButton::toggleState()
  850. {
  851. bool flipped = ! getToggleState();
  852. setToggleState(flipped);
  853. return flipped;
  854. }
  855. void LLButton::setLabel( const LLStringExplicit& label )
  856. {
  857. setLabelUnselected(label);
  858. setLabelSelected(label);
  859. }
  860. //virtual
  861. BOOL LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text )
  862. {
  863. mUnselectedLabel.setArg(key, text);
  864. mSelectedLabel.setArg(key, text);
  865. return TRUE;
  866. }
  867. void LLButton::setLabelUnselected( const LLStringExplicit& label )
  868. {
  869. mUnselectedLabel = label;
  870. }
  871. void LLButton::setLabelSelected( const LLStringExplicit& label )
  872. {
  873. mSelectedLabel = label;
  874. }
  875. bool LLButton::labelIsTruncated() const
  876. {
  877. return getCurrentLabel().getString().size() > mLastDrawCharsCount;
  878. }
  879. const LLUIString& LLButton::getCurrentLabel() const
  880. {
  881. if( getToggleState() )
  882. {
  883. return mSelectedLabel;
  884. }
  885. else
  886. {
  887. return mUnselectedLabel;
  888. }
  889. }
  890. void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
  891. {
  892. mImageUnselected = image;
  893. if (mImageUnselected.isNull())
  894. {
  895. llwarns << "Setting default button image for: " << getName() << " to NULL" << llendl;
  896. }
  897. }
  898. void LLButton::autoResize()
  899. {
  900. resize(getCurrentLabel());
  901. }
  902. void LLButton::resize(LLUIString label)
  903. {
  904. // get label length
  905. S32 label_width = mGLFont->getWidth(label.getString());
  906. // get current btn length
  907. S32 btn_width =getRect().getWidth();
  908. // check if it need resize
  909. if (mAutoResize)
  910. {
  911. S32 min_width = label_width + mLeftHPad + mRightHPad;
  912. if (mImageOverlay)
  913. {
  914. S32 overlay_width = mImageOverlay->getWidth();
  915. F32 scale_factor = (getRect().getHeight() - (mImageOverlayBottomPad + mImageOverlayTopPad)) / (F32)mImageOverlay->getHeight();
  916. overlay_width = llround((F32)overlay_width * scale_factor);
  917. switch(mImageOverlayAlignment)
  918. {
  919. case LLFontGL::LEFT:
  920. case LLFontGL::RIGHT:
  921. min_width += overlay_width + mImgOverlayLabelSpace;
  922. break;
  923. case LLFontGL::HCENTER:
  924. min_width = llmax(min_width, overlay_width + mLeftHPad + mRightHPad);
  925. break;
  926. default:
  927. // draw nothing
  928. break;
  929. }
  930. }
  931. if (btn_width < min_width)
  932. {
  933. reshape(min_width, getRect().getHeight());
  934. }
  935. }
  936. }
  937. void LLButton::setImages( const std::string &image_name, const std::string &selected_name )
  938. {
  939. setImageUnselected(LLUI::getUIImage(image_name));
  940. setImageSelected(LLUI::getUIImage(selected_name));
  941. }
  942. void LLButton::setImageSelected(LLPointer<LLUIImage> image)
  943. {
  944. mImageSelected = image;
  945. }
  946. void LLButton::setImageColor(const LLColor4& c)
  947. {
  948. mImageColor = c;
  949. }
  950. void LLButton::setColor(const LLColor4& color)
  951. {
  952. setImageColor(color);
  953. }
  954. void LLButton::setImageDisabled(LLPointer<LLUIImage> image)
  955. {
  956. mImageDisabled = image;
  957. mDisabledImageColor = mImageColor;
  958. mFadeWhenDisabled = TRUE;
  959. }
  960. void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image)
  961. {
  962. mImageDisabledSelected = image;
  963. mDisabledImageColor = mImageColor;
  964. mFadeWhenDisabled = TRUE;
  965. }
  966. void LLButton::setImagePressed(LLPointer<LLUIImage> image)
  967. {
  968. mImagePressed = image;
  969. }
  970. void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image)
  971. {
  972. mImageHoverSelected = image;
  973. }
  974. void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image)
  975. {
  976. mImageHoverUnselected = image;
  977. }
  978. void LLButton::setImageFlash(LLPointer<LLUIImage> image)
  979. {
  980. mImageFlash = image;
  981. }
  982. void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color)
  983. {
  984. if (image_name.empty())
  985. {
  986. mImageOverlay = NULL;
  987. }
  988. else
  989. {
  990. mImageOverlay = LLUI::getUIImage(image_name);
  991. mImageOverlayAlignment = alignment;
  992. mImageOverlayColor = color;
  993. }
  994. }
  995. void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color)
  996. {
  997. if (image_id.isNull())
  998. {
  999. mImageOverlay = NULL;
  1000. }
  1001. else
  1002. {
  1003. mImageOverlay = LLUI::getUIImageByID(image_id);
  1004. mImageOverlayAlignment = alignment;
  1005. mImageOverlayColor = color;
  1006. }
  1007. }
  1008. void LLButton::onMouseCaptureLost()
  1009. {
  1010. resetMouseDownTimer();
  1011. }
  1012. //-------------------------------------------------------------------------
  1013. // Utilities
  1014. //-------------------------------------------------------------------------
  1015. S32 round_up(S32 grid, S32 value)
  1016. {
  1017. S32 mod = value % grid;
  1018. if (mod > 0)
  1019. {
  1020. // not even multiple
  1021. return value + (grid - mod);
  1022. }
  1023. else
  1024. {
  1025. return value;
  1026. }
  1027. }
  1028. void LLButton::addImageAttributeToXML(LLXMLNodePtr node,
  1029. const std::string& image_name,
  1030. const LLUUID& image_id,
  1031. const std::string& xml_tag_name) const
  1032. {
  1033. if( !image_name.empty() )
  1034. {
  1035. node->createChild(xml_tag_name.c_str(), TRUE)->setStringValue(image_name);
  1036. }
  1037. else if( image_id != LLUUID::null )
  1038. {
  1039. node->createChild((xml_tag_name + "_id").c_str(), TRUE)->setUUIDValue(image_id);
  1040. }
  1041. }
  1042. // static
  1043. void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname)
  1044. {
  1045. bool floater_vis = LLFloaterReg::toggleInstance(sdname.asString());
  1046. LLButton* button = dynamic_cast<LLButton*>(ctrl);
  1047. if (button)
  1048. button->setToggleState(floater_vis);
  1049. }
  1050. // static
  1051. // Gets called once
  1052. void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
  1053. {
  1054. LLButton* button = dynamic_cast<LLButton*>(ctrl);
  1055. if (!button)
  1056. return;
  1057. // Get the visibility control name for the floater
  1058. std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
  1059. // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
  1060. button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
  1061. // Set the clicked callback to toggle the floater
  1062. button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD()));
  1063. }
  1064. // static
  1065. void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
  1066. {
  1067. LLButton* button = dynamic_cast<LLButton*>(ctrl);
  1068. if (!button)
  1069. return;
  1070. // Get the visibility control name for the floater
  1071. std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
  1072. // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
  1073. button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
  1074. // Set the clicked callback to toggle the floater
  1075. button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname));
  1076. }
  1077. // static
  1078. void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname)
  1079. {
  1080. // search back through the button's parents for a panel
  1081. // with a help_topic string defined
  1082. std::string help_topic;
  1083. if (LLUI::sHelpImpl &&
  1084. ctrl->findHelpTopic(help_topic))
  1085. {
  1086. LLUI::sHelpImpl->showTopic(help_topic);
  1087. return; // success
  1088. }
  1089. // display an error if we can't find a help_topic string.
  1090. // fix this by adding a help_topic attribute to the xui file
  1091. LLNotificationsUtil::add("UnableToFindHelpTopic");
  1092. }
  1093. void LLButton::resetMouseDownTimer()
  1094. {
  1095. mMouseDownTimer.stop();
  1096. mMouseDownTimer.reset();
  1097. }
  1098. BOOL LLButton::handleDoubleClick(S32 x, S32 y, MASK mask)
  1099. {
  1100. // just treat a double click as a second click
  1101. return handleMouseDown(x, y, mask);
  1102. }