llmenugl.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:100k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llmenugl.cpp
  3.  * @brief LLMenuItemGL base class
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. //*****************************************************************************
  33. //
  34. // This file contains the opengl based menu implementation.
  35. //
  36. // NOTES: A menu label is split into 4 columns. The left column, the
  37. // label colum, the accelerator column, and the right column. The left
  38. // column is used for displaying boolean values for toggle and check
  39. // controls. The right column is used for submenus.
  40. //
  41. //*****************************************************************************
  42. //#include "llviewerprecompiledheaders.h"
  43. #include "linden_common.h"
  44. #include "llmenugl.h"
  45. #include "llgl.h"
  46. #include "llmath.h"
  47. #include "llrender.h"
  48. #include "llfocusmgr.h"
  49. #include "llcoord.h"
  50. #include "llwindow.h"
  51. #include "llcriticaldamp.h"
  52. #include "lluictrlfactory.h"
  53. #include "llbutton.h"
  54. #include "llfontgl.h"
  55. #include "llresmgr.h"
  56. #include "llui.h"
  57. #include "llstl.h"
  58. #include "v2math.h"
  59. #include <set>
  60. #include <boost/tokenizer.hpp>
  61. // static
  62. LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
  63. S32 MENU_BAR_HEIGHT = 0;
  64. S32 MENU_BAR_WIDTH = 0;
  65. ///============================================================================
  66. /// Local function declarations, constants, enums, and typedefs
  67. ///============================================================================
  68. const std::string SEPARATOR_NAME("separator");
  69. const std::string SEPARATOR_LABEL( "-----------" );
  70. const std::string VERTICAL_SEPARATOR_LABEL( "|" );
  71. const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
  72. const U32 LEFT_PAD_PIXELS = 3;
  73. const U32 LEFT_WIDTH_PIXELS = 15;
  74. const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
  75. const U32 RIGHT_PAD_PIXELS = 2;
  76. const U32 RIGHT_WIDTH_PIXELS = 15;
  77. const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
  78. const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
  79. const U32 BRIEF_PAD_PIXELS = 2;
  80. const U32 SEPARATOR_HEIGHT_PIXELS = 8;
  81. const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
  82. const S32 MENU_ITEM_PADDING = 4;
  83. const std::string BOOLEAN_TRUE_PREFIX( "xE2x9Cx94" ); // U+2714 HEAVY CHECK MARK
  84. const std::string BRANCH_SUFFIX( "xE2x96xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE
  85. const std::string ARROW_UP  ("^^^^^^^");
  86. const std::string ARROW_DOWN("vvvvvvv");
  87. const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
  88. const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10;
  89. BOOL LLMenuGL::sKeyboardMode = FALSE;
  90. LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
  91. LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
  92. //LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f );
  93. const S32 PIE_CENTER_SIZE = 20; // pixels, radius of center hole
  94. const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down
  95. const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu
  96. const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
  97. static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
  98. static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
  99. static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
  100. static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
  101. // Created programmatically but we need to specify custom colors in xml
  102. static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off");
  103. static MenuRegistry::Register<LLMenuGL> register_menu("menu");
  104. static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
  105. ///============================================================================
  106. /// Class LLMenuItemGL
  107. ///============================================================================
  108. LLMenuItemGL::Params::Params()
  109. : shortcut("shortcut"),
  110. jump_key("jump_key", KEY_NONE),
  111. use_mac_ctrl("use_mac_ctrl", false),
  112. rect("rect"),
  113. left("left"),
  114. top("top"),
  115. right("right"),
  116. bottom("bottom"),
  117. width("width"),
  118. height("height"),
  119. bottom_delta("bottom_delta"),
  120. left_delta("left_delta"),
  121. enabled_color("enabled_color"),
  122. disabled_color("disabled_color"),
  123. highlight_bg_color("highlight_bg_color"),
  124. highlight_fg_color("highlight_fg_color")
  125. {
  126. mouse_opaque = true;
  127. }
  128. // Default constructor
  129. LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
  130. : LLUICtrl(p),
  131. mJumpKey(p.jump_key),
  132. mAllowKeyRepeat(FALSE),
  133. mHighlight( FALSE ),
  134. mGotHover( FALSE ),
  135. mBriefItem( FALSE ),
  136. mDrawTextDisabled( FALSE ),
  137. mFont(p.font),
  138. mAcceleratorKey(KEY_NONE),
  139. mAcceleratorMask(MASK_NONE),
  140. mLabel(p.label.isProvided() ? p.label() : p.name()),
  141. mEnabledColor(p.enabled_color()),
  142. mDisabledColor(p.disabled_color()),
  143. mHighlightBackground(p.highlight_bg_color()),
  144. mHighlightForeground(p.highlight_fg_color())
  145. {
  146. #ifdef LL_DARWIN
  147. // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd
  148. BOOL useMacCtrl = p.use_mac_ctrl;
  149. #endif // LL_DARWIN
  150. std::string shortcut = p.shortcut;
  151. if (shortcut.find("control") != shortcut.npos)
  152. {
  153. #ifdef LL_DARWIN
  154. if ( useMacCtrl )
  155. {
  156. mAcceleratorMask |= MASK_MAC_CONTROL;
  157. }
  158. #endif // LL_DARWIN
  159. mAcceleratorMask |= MASK_CONTROL;
  160. }
  161. if (shortcut.find("alt") != shortcut.npos)
  162. {
  163. mAcceleratorMask |= MASK_ALT;
  164. }
  165. if (shortcut.find("shift") != shortcut.npos)
  166. {
  167. mAcceleratorMask |= MASK_SHIFT;
  168. }
  169. S32 pipe_pos = shortcut.rfind("|");
  170. std::string key_str = shortcut.substr(pipe_pos+1);
  171. LLKeyboard::keyFromString(key_str, &mAcceleratorKey);
  172. LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut
  173. << ", key str: " << key_str
  174. << ", accelerator mask: " << mAcceleratorMask
  175. << ", accelerator key: " << mAcceleratorKey
  176. << LL_ENDL;
  177. }
  178. //virtual
  179. void LLMenuItemGL::setValue(const LLSD& value)
  180. {
  181. setLabel(value.asString());
  182. }
  183. //virtual
  184. BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
  185. {
  186. if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
  187. {
  188. onCommit();
  189. return TRUE;
  190. }
  191. return FALSE;
  192. }
  193. BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
  194. {
  195. setHover(TRUE);
  196. getWindow()->setCursor(UI_CURSOR_ARROW);
  197. return TRUE;
  198. }
  199. //virtual
  200. BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
  201. {
  202. return LLUICtrl::handleRightMouseDown(x,y,mask);
  203. }
  204. //virtual
  205. BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
  206. {
  207. // If this event came from a right-click context menu spawn,
  208. // process as a left-click to allow menu items to be hit
  209. if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
  210. || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
  211. {
  212. BOOL handled = handleMouseUp(x, y, mask);
  213. return handled;
  214. }
  215. return LLUICtrl::handleRightMouseUp(x,y,mask);
  216. }
  217. // This function checks to see if the accelerator key is already in use;
  218. // if not, it will be added to the list
  219. BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
  220. {
  221. LLKeyBinding *accelerator = NULL;
  222. if (mAcceleratorKey != KEY_NONE)
  223. {
  224. std::list<LLKeyBinding*>::iterator list_it;
  225. for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
  226. {
  227. accelerator = *list_it;
  228. if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS)))
  229. {
  230. // *NOTE: get calling code to throw up warning or route
  231. // warning messages back to app-provided output
  232. // std::string warning;
  233. // warning.append("Duplicate key binding <");
  234. // appendAcceleratorString( warning );
  235. // warning.append("> for menu items:n    ");
  236. // warning.append(accelerator->mName);
  237. // warning.append("n    ");
  238. // warning.append(mLabel);
  239. // llwarns << warning << llendl;
  240. // LLAlertDialog::modalAlert(warning);
  241. return FALSE;
  242. }
  243. }
  244. if (!accelerator)
  245. {
  246. accelerator = new LLKeyBinding;
  247. if (accelerator)
  248. {
  249. accelerator->mKey = mAcceleratorKey;
  250. accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
  251. //  accelerator->mName = mLabel;
  252. }
  253. listp->push_back(accelerator);//addData(accelerator);
  254. }
  255. }
  256. return TRUE;
  257. }
  258. // This function appends the character string representation of
  259. // the current accelerator key and mask to the provided string.
  260. void LLMenuItemGL::appendAcceleratorString( std::string& st ) const
  261. {
  262. st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey );
  263. LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL;
  264. }
  265. void LLMenuItemGL::setJumpKey(KEY key)
  266. {
  267. mJumpKey = LLStringOps::toUpper((char)key);
  268. }
  269. // virtual 
  270. U32 LLMenuItemGL::getNominalHeight( void ) const 
  271. return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; 
  272. }
  273. //virtual
  274. void LLMenuItemGL::setBriefItem(BOOL brief)
  275. {
  276. mBriefItem = brief;
  277. }
  278. //virtual
  279. BOOL LLMenuItemGL::isBriefItem() const
  280. {
  281. return mBriefItem;
  282. }
  283. // Get the parent menu for this item
  284. LLMenuGL* LLMenuItemGL::getMenu() const
  285. {
  286. return (LLMenuGL*) getParent();
  287. }
  288. // getNominalWidth() - returns the normal width of this control in
  289. // pixels - this is used for calculating the widest item, as well as
  290. // for horizontal arrangement.
  291. U32 LLMenuItemGL::getNominalWidth( void ) const
  292. {
  293. U32 width;
  294. if (mBriefItem)
  295. {
  296. width = BRIEF_PAD_PIXELS;
  297. }
  298. else
  299. {
  300. width = PLAIN_PAD_PIXELS;
  301. }
  302. if( KEY_NONE != mAcceleratorKey )
  303. {
  304. width += getMenu()->getShortcutPad();
  305. std::string temp;
  306. appendAcceleratorString( temp );
  307. width += mFont->getWidth( temp );
  308. }
  309. width += mFont->getWidth( mLabel.getWString().c_str() );
  310. return width;
  311. }
  312. // called to rebuild the draw label
  313. void LLMenuItemGL::buildDrawLabel( void )
  314. {
  315. mDrawAccelLabel.clear();
  316. std::string st = mDrawAccelLabel.getString();
  317. appendAcceleratorString( st );
  318. mDrawAccelLabel = st;
  319. }
  320. void LLMenuItemGL::onCommit( void )
  321. {
  322. // Check torn-off status to allow left-arrow keyboard navigation back
  323. // to parent menu.
  324. // Also, don't hide if item triggered by keyboard shortcut (and hence
  325. // parent not visible).
  326. if (!getMenu()->getTornOff() 
  327. && getMenu()->getVisible())
  328. {
  329. LLMenuGL::sMenuContainer->hideMenus();
  330. }
  331. LLUICtrl::onCommit();
  332. }
  333. // set the hover status (called by it's menu)
  334.  void LLMenuItemGL::setHighlight( BOOL highlight )
  335. {
  336. if (highlight)
  337. {
  338. getMenu()->clearHoverItem();
  339. }
  340. if (mHighlight != highlight)
  341. {
  342. dirtyRect();
  343. }
  344. mHighlight = highlight;
  345. }
  346. BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask )
  347. {
  348. if (getHighlight() && 
  349. getMenu()->isOpen())
  350. {
  351. if (key == KEY_UP)
  352. {
  353. // switch to keyboard navigation mode
  354. LLMenuGL::setKeyboardMode(TRUE);
  355. getMenu()->highlightPrevItem(this);
  356. return TRUE;
  357. }
  358. else if (key == KEY_DOWN)
  359. {
  360. // switch to keyboard navigation mode
  361. LLMenuGL::setKeyboardMode(TRUE);
  362. getMenu()->highlightNextItem(this);
  363. return TRUE;
  364. }
  365. else if (key == KEY_RETURN && mask == MASK_NONE)
  366. {
  367. // switch to keyboard navigation mode
  368. LLMenuGL::setKeyboardMode(TRUE);
  369. onCommit();
  370. return TRUE;
  371. }
  372. }
  373. return FALSE;
  374. }
  375. BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask)
  376. {
  377. // switch to mouse navigation mode
  378. LLMenuGL::setKeyboardMode(FALSE);
  379. onCommit();
  380. make_ui_sound("UISndClickRelease");
  381. return LLView::handleMouseUp(x, y, mask);
  382. }
  383. BOOL LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask)
  384. {
  385. // switch to mouse navigation mode
  386. LLMenuGL::setKeyboardMode(FALSE);
  387. setHighlight(TRUE);
  388. return LLView::handleMouseDown(x, y, mask);
  389. }
  390. BOOL LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
  391. {
  392. // If the menu is scrollable let it handle the wheel event.
  393. return !getMenu()->isScrollable();
  394. }
  395. void LLMenuItemGL::draw( void )
  396. {
  397. // *FIX: This can be optimized by using switches. Want to avoid
  398. // that until the functionality is finalized.
  399. // HACK: Brief items don't highlight.  Pie menu takes care of it.  JC
  400. // let disabled items be highlighted, just don't draw them as such
  401. if( getEnabled() && getHighlight() && !mBriefItem)
  402. {
  403. int debug_count = 0;
  404. if (dynamic_cast<LLMenuItemCallGL*>(this))
  405. debug_count++;
  406. gGL.color4fv( mHighlightBackground.get().mV );
  407. gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
  408. }
  409. LLColor4 color;
  410. if ( getEnabled() && getHighlight() )
  411. {
  412. color = mHighlightForeground.get();
  413. }
  414. else if( getEnabled() && !mDrawTextDisabled )
  415. {
  416. color = mEnabledColor.get();
  417. }
  418. else
  419. {
  420. color = mDisabledColor.get();
  421. }
  422. // Draw the text on top.
  423. if (mBriefItem)
  424. {
  425. mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
  426.    LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL);
  427. }
  428. else
  429. {
  430. if( !mDrawBoolLabel.empty() )
  431. {
  432. mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
  433.    LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
  434. }
  435. mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
  436.    LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
  437. if( !mDrawAccelLabel.empty() )
  438. {
  439. mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
  440.    LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
  441. }
  442. if( !mDrawBranchLabel.empty() )
  443. {
  444. mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
  445.    LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
  446. }
  447. }
  448. // underline "jump" key only when keyboard navigation has been initiated
  449. if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
  450. {
  451. std::string upper_case_label = mLabel.getString();
  452. LLStringUtil::toUpper(upper_case_label);
  453. std::string::size_type offset = upper_case_label.find(mJumpKey);
  454. if (offset != std::string::npos)
  455. {
  456. S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
  457. S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
  458. gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
  459. }
  460. }
  461. // clear got hover every frame
  462. setHover(FALSE);
  463. }
  464. BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text )
  465. {
  466. mLabel.setArg(key, text);
  467. return TRUE;
  468. }
  469. void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility)
  470. {
  471. if (getMenu())
  472. {
  473. getMenu()->needsArrange();
  474. }
  475. LLView::handleVisibilityChange(new_visibility);
  476. }
  477. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  478. // Class LLMenuItemSeparatorGL
  479. //
  480. // This class represents a separator.
  481. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  482. LLMenuItemSeparatorGL::Params::Params()
  483. {
  484. name = "separator";
  485. label = SEPARATOR_LABEL;
  486. }
  487. LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
  488. LLMenuItemGL( p )
  489. {
  490. }
  491. //virtual
  492. U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const
  493. {
  494. return SEPARATOR_HEIGHT_PIXELS;
  495. }
  496. void LLMenuItemSeparatorGL::draw( void )
  497. {
  498. gGL.color4fv( mDisabledColor.get().mV );
  499. const S32 y = getRect().getHeight() / 2;
  500. const S32 PAD = 6;
  501. gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
  502. }
  503. BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
  504. {
  505. LLMenuGL* parent_menu = getMenu();
  506. if (y > getRect().getHeight() / 2)
  507. {
  508. // the menu items are in the child list in bottom up order
  509. LLView* prev_menu_item = parent_menu->findNextSibling(this);
  510. return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
  511. }
  512. else
  513. {
  514. LLView* next_menu_item = parent_menu->findPrevSibling(this);
  515. return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE;
  516. }
  517. }
  518. BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) 
  519. {
  520. LLMenuGL* parent_menu = getMenu();
  521. if (y > getRect().getHeight() / 2)
  522. {
  523. LLView* prev_menu_item = parent_menu->findNextSibling(this);
  524. return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
  525. }
  526. else
  527. {
  528. LLView* next_menu_item = parent_menu->findPrevSibling(this);
  529. return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE;
  530. }
  531. }
  532. BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) 
  533. {
  534. LLMenuGL* parent_menu = getMenu();
  535. if (y > getRect().getHeight() / 2)
  536. {
  537. parent_menu->highlightPrevItem(this, FALSE);
  538. return FALSE;
  539. }
  540. else
  541. {
  542. parent_menu->highlightNextItem(this, FALSE);
  543. return FALSE;
  544. }
  545. }
  546. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  547. // Class LLMenuItemVerticalSeparatorGL
  548. //
  549. // This class represents a vertical separator.
  550. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  551. class LLMenuItemVerticalSeparatorGL
  552. : public LLMenuItemSeparatorGL
  553. {
  554. public:
  555. LLMenuItemVerticalSeparatorGL( void );
  556. virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
  557. };
  558. LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
  559. {
  560. setLabel( VERTICAL_SEPARATOR_LABEL );
  561. }
  562. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  563. // Class LLMenuItemTearOffGL
  564. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  565. LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) 
  566. : LLMenuItemGL(p)
  567. {
  568. }
  569. // Returns the first floater ancestor if there is one
  570. LLFloater* LLMenuItemTearOffGL::getParentFloater()
  571. {
  572. LLView* parent_view = getMenu();
  573. while (parent_view)
  574. {
  575. if (dynamic_cast<LLFloater*>(parent_view))
  576. {
  577. return dynamic_cast<LLFloater*>(parent_view);
  578. }
  579. bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
  580. if (parent_is_menu)
  581. {
  582. // use menu parent
  583. parent_view =  dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
  584. }
  585. else
  586. {
  587. // just use regular view parent
  588. parent_view = parent_view->getParent();
  589. }
  590. }
  591. return NULL;
  592. }
  593. void LLMenuItemTearOffGL::onCommit()
  594. {
  595. if (getMenu()->getTornOff())
  596. {
  597. LLTearOffMenu* torn_off_menu = (LLTearOffMenu*)(getMenu()->getParent());
  598. torn_off_menu->closeFloater();
  599. }
  600. else
  601. {
  602. // transfer keyboard focus and highlight to first real item in list
  603. if (getHighlight())
  604. {
  605. getMenu()->highlightNextItem(this);
  606. }
  607. getMenu()->needsArrange();
  608. LLFloater* parent_floater = getParentFloater();
  609. LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
  610. if (tear_off_menu)
  611. {
  612. if (parent_floater)
  613. {
  614. parent_floater->addDependentFloater(tear_off_menu, FALSE);
  615. }
  616. // give focus to torn off menu because it will have
  617. // been taken away when parent menu closes
  618. tear_off_menu->setFocus(TRUE);
  619. }
  620. }
  621. LLMenuItemGL::onCommit();
  622. }
  623. void LLMenuItemTearOffGL::draw()
  624. {
  625. // disabled items can be highlighted, but shouldn't render as such
  626. if( getEnabled() && getHighlight() && !isBriefItem())
  627. {
  628. gGL.color4fv( mHighlightBackground.get().mV );
  629. gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
  630. }
  631. if (getEnabled())
  632. {
  633. gGL.color4fv( mEnabledColor.get().mV );
  634. }
  635. else
  636. {
  637. gGL.color4fv( mDisabledColor.get().mV );
  638. }
  639. const S32 y = getRect().getHeight() / 3;
  640. const S32 PAD = 6;
  641. gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
  642. gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 );
  643. }
  644. U32 LLMenuItemTearOffGL::getNominalHeight( void ) const 
  645. return TEAROFF_SEPARATOR_HEIGHT_PIXELS; 
  646. }
  647. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  648. // Class LLMenuItemBlankGL
  649. //
  650. // This class represents a blank, non-functioning item.
  651. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  652. class LLMenuItemBlankGL : public LLMenuItemGL
  653. {
  654. public:
  655. struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
  656. {
  657. Params()
  658. {
  659. name="";
  660. enabled = false;
  661. }
  662. };
  663. LLMenuItemBlankGL( const Params& p ) : LLMenuItemGL( p )
  664. {}
  665. virtual void draw( void ) {}
  666. };
  667. ///============================================================================
  668. /// Class LLMenuItemCallGL
  669. ///============================================================================
  670. LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
  671. : LLMenuItemGL(p)
  672. {
  673. }
  674. void LLMenuItemCallGL::initFromParams(const Params& p)
  675. {
  676. if (p.on_visible.isProvided())
  677. {
  678. mVisibleSignal.connect(initEnableCallback(p.on_visible));
  679. }
  680. if (p.on_enable.isProvided())
  681. {
  682. setEnableCallback(initEnableCallback(p.on_enable));
  683. // Set the enabled control variable (for backwards compatability)
  684. if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty())
  685. {
  686. LLControlVariable* control = findControl(p.on_enable.control_name());
  687. if (control)
  688. {
  689. setEnabledControlVariable(control);
  690. }
  691. }
  692. }
  693. if (p.on_click.isProvided())
  694. {
  695. setCommitCallback(initCommitCallback(p.on_click));
  696. }
  697. LLUICtrl::initFromParams(p);
  698. }
  699. void LLMenuItemCallGL::onCommit( void )
  700. {
  701. // RN: menu item can be deleted in callback, so beware
  702. getMenu()->setItemLastSelected( this );
  703. LLMenuItemGL::onCommit();
  704. }
  705. void LLMenuItemCallGL::updateEnabled( void )
  706. {
  707. if (mEnableSignal.num_slots() > 0)
  708. {
  709. bool enabled = mEnableSignal(this, LLSD());
  710. if (mEnabledControlVariable)
  711. {
  712. if (!enabled)
  713. {
  714. // callback overrides control variable; this will call setEnabled()
  715. mEnabledControlVariable->set(false); 
  716. }
  717. }
  718. else
  719. {
  720. setEnabled(enabled);
  721. }
  722. }
  723. }
  724. void LLMenuItemCallGL::updateVisible( void )
  725. {
  726. if (mVisibleSignal.num_slots() > 0)
  727. {
  728. bool visible = mVisibleSignal(this, LLSD());
  729. setVisible(visible);
  730. }
  731. }
  732. void LLMenuItemCallGL::buildDrawLabel( void )
  733. {
  734. updateEnabled();
  735. updateVisible();
  736. LLMenuItemGL::buildDrawLabel();
  737. }
  738. BOOL LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask )
  739. {
  740. return LLMenuItemGL::handleKeyHere(key, mask);
  741. }
  742. BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
  743. {
  744. if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
  745. {
  746. updateEnabled();
  747. if (getEnabled())
  748. {
  749. onCommit();
  750. return TRUE;
  751. }
  752. }
  753. return FALSE;
  754. }
  755. // handleRightMouseUp moved into base class LLMenuItemGL so clicks are
  756. // handled for all menu item types
  757. ///============================================================================
  758. /// Class LLMenuItemCheckGL
  759. ///============================================================================
  760. LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p)
  761. : LLMenuItemCallGL(p)
  762. {
  763. }
  764. void LLMenuItemCheckGL::initFromParams(const Params& p)
  765. {
  766. if (p.on_check.isProvided())
  767. {
  768. setCheckCallback(initEnableCallback(p.on_check));
  769. // Set the control name (for backwards compatability)
  770. if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty())
  771. {
  772. setControlName(p.on_check.control_name());
  773. }
  774. }
  775. LLMenuItemCallGL::initFromParams(p);
  776. }
  777. void LLMenuItemCheckGL::onCommit( void )
  778. {
  779. LLMenuItemCallGL::onCommit();
  780. }
  781. //virtual
  782. void LLMenuItemCheckGL::setValue(const LLSD& value)
  783. {
  784. LLUICtrl::setValue(value);
  785. if(value.asBoolean())
  786. {
  787. mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
  788. }
  789. else
  790. {
  791. mDrawBoolLabel.clear();
  792. }
  793. }
  794. // called to rebuild the draw label
  795. void LLMenuItemCheckGL::buildDrawLabel( void )
  796. {
  797. // Note: mCheckSignal() returns true if no callbacks are set
  798. bool checked = mCheckSignal(this, LLSD());
  799. if (mControlVariable)
  800. {
  801. if (!checked) 
  802. setControlValue(false); // callback overrides control variable; this will call setValue()
  803. }
  804. else
  805. {
  806. setValue(checked);
  807. }
  808. if(getValue().asBoolean())
  809. {
  810. mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
  811. }
  812. else
  813. {
  814. mDrawBoolLabel.clear();
  815. }
  816. LLMenuItemCallGL::buildDrawLabel();
  817. }
  818. ///============================================================================
  819. /// Class LLMenuItemBranchGL
  820. ///============================================================================
  821. LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p)
  822.   : LLMenuItemGL(p)
  823. {
  824. LLMenuGL* branch = p.branch;
  825. if (branch)
  826. {
  827. mBranchHandle = branch->getHandle();
  828. branch->setVisible(FALSE);
  829. branch->setParentMenuItem(this);
  830. }
  831. }
  832. LLMenuItemBranchGL::~LLMenuItemBranchGL()
  833. {
  834. LLView::deleteViewByHandle(mBranchHandle);
  835. }
  836. // virtual
  837. LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const
  838. {
  839. LLMenuGL* branch = getBranch();
  840. if (branch)
  841. {
  842. if (branch->getName() == name)
  843. {
  844. return branch;
  845. }
  846. // Always recurse on branches
  847. return branch->getChildView(name, recurse);
  848. }
  849. return LLView::getChildView(name, recurse);
  850. }
  851. LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const
  852. {
  853. LLMenuGL* branch = getBranch();
  854. if (branch)
  855. {
  856. if (branch->getName() == name)
  857. {
  858. return branch;
  859. }
  860. // Always recurse on branches
  861. return branch->findChildView(name, recurse);
  862. }
  863. return LLView::findChildView(name, recurse);
  864. }
  865. // virtual
  866. BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
  867. {
  868. // switch to mouse navigation mode
  869. LLMenuGL::setKeyboardMode(FALSE);
  870. onCommit();
  871. make_ui_sound("UISndClickRelease");
  872. return TRUE;
  873. }
  874. BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
  875. {
  876. return getBranch() && getBranch()->handleAcceleratorKey(key, mask);
  877. }
  878. // This function checks to see if the accelerator key is already in use;
  879. // if not, it will be added to the list
  880. BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*> *listp)
  881. {
  882. LLMenuGL* branch = getBranch();
  883. if (!branch)
  884. return FALSE;
  885. U32 item_count = branch->getItemCount();
  886. LLMenuItemGL *item;
  887. while (item_count--)
  888. {
  889. if ((item = branch->getItem(item_count)))
  890. {
  891. return item->addToAcceleratorList(listp);
  892. }
  893. }
  894. return FALSE;
  895. }
  896. // called to rebuild the draw label
  897. void LLMenuItemBranchGL::buildDrawLabel( void )
  898. {
  899. mDrawAccelLabel.clear();
  900. std::string st = mDrawAccelLabel;
  901. appendAcceleratorString( st );
  902. mDrawAccelLabel = st;
  903. mDrawBranchLabel = BRANCH_SUFFIX;
  904. }
  905. void LLMenuItemBranchGL::onCommit( void )
  906. {
  907. openMenu();
  908. // keyboard navigation automatically propagates highlight to sub-menu
  909. // to facilitate fast menu control via jump keys
  910. if (LLMenuGL::getKeyboardMode() && getBranch()&& !getBranch()->getHighlightedItem())
  911. {
  912. getBranch()->highlightNextItem(NULL);
  913. }
  914. LLUICtrl::onCommit();
  915. }
  916. BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
  917. {
  918. BOOL handled = FALSE;
  919. if (getBranch() && called_from_parent)
  920. {
  921. handled = getBranch()->handleKey(key, mask, called_from_parent);
  922. }
  923. if (!handled)
  924. {
  925. handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
  926. }
  927. return handled;
  928. }
  929. BOOL LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
  930. {
  931. BOOL handled = FALSE;
  932. if (getBranch() && called_from_parent)
  933. {
  934. handled = getBranch()->handleUnicodeChar(uni_char, TRUE);
  935. }
  936. if (!handled)
  937. {
  938. handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent);
  939. }
  940. return handled;
  941. }
  942. void LLMenuItemBranchGL::setHighlight( BOOL highlight )
  943. {
  944. if (highlight == getHighlight())
  945. return;
  946. LLMenuGL* branch = getBranch();
  947. if (!branch)
  948. return;
  949. BOOL auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff());
  950. // torn off menus don't open sub menus on hover unless they have focus
  951. if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus())
  952. {
  953. auto_open = FALSE;
  954. }
  955. // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
  956. if (branch->getTornOff())
  957. {
  958. auto_open = FALSE;
  959. }
  960. LLMenuItemGL::setHighlight(highlight);
  961. if( highlight )
  962. {
  963. if(auto_open)
  964. {
  965. openMenu();
  966. }
  967. }
  968. else
  969. {
  970. if (branch->getTornOff())
  971. {
  972. ((LLFloater*)branch->getParent())->setFocus(FALSE);
  973. branch->clearHoverItem();
  974. }
  975. else
  976. {
  977. branch->setVisible( FALSE );
  978. }
  979. }
  980. }
  981. void LLMenuItemBranchGL::draw()
  982. {
  983. LLMenuItemGL::draw();
  984. if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff())
  985. {
  986. setHighlight(TRUE);
  987. }
  988. }
  989. void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
  990. {
  991. if (getBranch() && getBranch()->getParent() == NULL)
  992. {
  993. // make the branch menu a sibling of my parent menu
  994. getBranch()->updateParent(parentp);
  995. }
  996. }
  997. void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility )
  998. {
  999. if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff())
  1000. {
  1001. getBranch()->setVisible(FALSE);
  1002. }
  1003. LLMenuItemGL::handleVisibilityChange(new_visibility);
  1004. }
  1005. BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
  1006. {
  1007. LLMenuGL* branch = getBranch();
  1008. if (!branch)
  1009. return LLMenuItemGL::handleKeyHere(key, mask);
  1010. // an item is highlighted, my menu is open, and I have an active sub menu or we are in
  1011. // keyboard navigation mode
  1012. if (getHighlight() 
  1013. && getMenu()->isOpen() 
  1014. && (isActive() || LLMenuGL::getKeyboardMode()))
  1015. {
  1016. if (branch->getVisible() && key == KEY_LEFT)
  1017. {
  1018. // switch to keyboard navigation mode
  1019. LLMenuGL::setKeyboardMode(TRUE);
  1020. BOOL handled = branch->clearHoverItem();
  1021. if (branch->getTornOff())
  1022. {
  1023. ((LLFloater*)branch->getParent())->setFocus(FALSE);
  1024. }
  1025. if (handled && getMenu()->getTornOff())
  1026. {
  1027. ((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
  1028. }
  1029. return handled;
  1030. }
  1031. if (key == KEY_RIGHT && !branch->getHighlightedItem())
  1032. {
  1033. // switch to keyboard navigation mode
  1034. LLMenuGL::setKeyboardMode(TRUE);
  1035. LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
  1036. if (itemp)
  1037. {
  1038. return TRUE;
  1039. }
  1040. }
  1041. }
  1042. return LLMenuItemGL::handleKeyHere(key, mask);
  1043. }
  1044. //virtual
  1045. BOOL LLMenuItemBranchGL::isActive() const
  1046. {
  1047. return isOpen() && getBranch() && getBranch()->getHighlightedItem();
  1048. }
  1049. //virtual
  1050. BOOL LLMenuItemBranchGL::isOpen() const
  1051. {
  1052. return getBranch() && getBranch()->isOpen();
  1053. }
  1054. void LLMenuItemBranchGL::openMenu()
  1055. {
  1056. LLMenuGL* branch = getBranch();
  1057. if (!branch)
  1058. return;
  1059. if (branch->getTornOff())
  1060. {
  1061. gFloaterView->bringToFront((LLFloater*)branch->getParent());
  1062. // this might not be necessary, as torn off branches don't get focus and hence no highligth
  1063. branch->highlightNextItem(NULL);
  1064. }
  1065. else if( !branch->getVisible() )
  1066. {
  1067. // get valid rectangle for menus
  1068. const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
  1069. branch->arrange();
  1070. LLRect branch_rect = branch->getRect();
  1071. // calculate root-view relative position for branch menu
  1072. S32 left = getRect().mRight;
  1073. S32 top = getRect().mTop - getRect().mBottom;
  1074. localPointToOtherView(left, top, &left, &top, branch->getParent());
  1075. branch_rect.setLeftTopAndSize( left, top,
  1076. branch_rect.getWidth(), branch_rect.getHeight() );
  1077. if (branch->getCanTearOff())
  1078. {
  1079. branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
  1080. }
  1081. branch->setRect( branch_rect );
  1082. // if branch extends outside of menu region change the direction it opens in
  1083. S32 x, y;
  1084. S32 delta_x = 0;
  1085. S32 delta_y = 0;
  1086. branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); 
  1087. if( y < menu_region_rect.mBottom )
  1088. {
  1089. // open upwards if menu extends past bottom
  1090. // adjust by the height of the menu item branch since it is a submenu
  1091. delta_y = branch_rect.getHeight() - getRect().getHeight();
  1092. }
  1093. if( x + branch_rect.getWidth() > menu_region_rect.mRight )
  1094. {
  1095. // move sub-menu over to left side
  1096. delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth())));
  1097. }
  1098. branch->translate( delta_x, delta_y );
  1099. branch->setVisible( TRUE );
  1100. branch->getParent()->sendChildToFront(branch);
  1101. dirtyRect();
  1102. }
  1103. }
  1104. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1105. // Class LLMenuItemBranchDownGL
  1106. //
  1107. // The LLMenuItemBranchDownGL represents a menu item that has a
  1108. // sub-menu. This is used to make menu bar menus.
  1109. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1110. class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
  1111. {
  1112. protected:
  1113. public:
  1114. LLMenuItemBranchDownGL( const Params& );
  1115. // returns the normal width of this control in pixels - this is
  1116. // used for calculating the widest item, as well as for horizontal
  1117. // arrangement.
  1118. virtual U32 getNominalWidth( void ) const;
  1119. // called to rebuild the draw label
  1120. virtual void buildDrawLabel( void );
  1121. // handles opening, positioning, and arranging the menu branch associated with this item
  1122. virtual void openMenu( void );
  1123. // set the hover status (called by it's menu) and if the object is
  1124. // active. This is used for behavior transfer.
  1125. virtual void setHighlight( BOOL highlight );
  1126. virtual BOOL isActive( void ) const;
  1127. // LLView functionality
  1128. virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
  1129. virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); 
  1130. virtual void draw( void );
  1131. virtual BOOL handleKeyHere(KEY key, MASK mask);
  1132. virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
  1133. };
  1134. LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) :
  1135. LLMenuItemBranchGL(p)
  1136. {
  1137. }
  1138. // returns the normal width of this control in pixels - this is used
  1139. // for calculating the widest item, as well as for horizontal
  1140. // arrangement.
  1141. U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const
  1142. {
  1143. U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
  1144. width += getFont()->getWidth( mLabel.getWString().c_str() ); 
  1145. return width;
  1146. }
  1147. // called to rebuild the draw label
  1148. void LLMenuItemBranchDownGL::buildDrawLabel( void )
  1149. {
  1150. mDrawAccelLabel.clear();
  1151. std::string st = mDrawAccelLabel;
  1152. appendAcceleratorString( st );
  1153. mDrawAccelLabel = st;
  1154. }
  1155. void LLMenuItemBranchDownGL::openMenu( void )
  1156. {
  1157. LLMenuGL* branch = getBranch();
  1158. if( branch->getVisible() && !branch->getTornOff() )
  1159. {
  1160. branch->setVisible( FALSE );
  1161. }
  1162. else
  1163. {
  1164. if (branch->getTornOff())
  1165. {
  1166. gFloaterView->bringToFront((LLFloater*)branch->getParent());
  1167. }
  1168. else
  1169. {
  1170. // We're showing the drop-down menu, so patch up its labels/rects
  1171. branch->arrange();
  1172. LLRect rect = branch->getRect();
  1173. S32 left = 0;
  1174. S32 top = getRect().mBottom;
  1175. localPointToOtherView(left, top, &left, &top, branch->getParent());
  1176. rect.setLeftTopAndSize( left, top,
  1177. rect.getWidth(), rect.getHeight() );
  1178. branch->setRect( rect );
  1179. S32 x = 0;
  1180. S32 y = 0;
  1181. branch->localPointToScreen( 0, 0, &x, &y ); 
  1182. S32 delta_x = 0;
  1183. LLCoordScreen window_size;
  1184. LLWindow* windowp = getWindow();
  1185. windowp->getSize(&window_size);
  1186. S32 window_width = window_size.mX;
  1187. if( x > window_width - rect.getWidth() )
  1188. {
  1189. delta_x = (window_width - rect.getWidth()) - x;
  1190. }
  1191. branch->translate( delta_x, 0 );
  1192. setHighlight(TRUE);
  1193. branch->setVisible( TRUE );
  1194. branch->getParent()->sendChildToFront(branch);
  1195. }
  1196. }
  1197. }
  1198. // set the hover status (called by it's menu)
  1199. void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
  1200. {
  1201.   if (highlight == getHighlight())
  1202. return;
  1203. //NOTE: Purposely calling all the way to the base to bypass auto-open.
  1204. LLMenuItemGL::setHighlight(highlight);
  1205. LLMenuGL* branch = getBranch();
  1206. if (!branch)
  1207. return;
  1208. if( !highlight)
  1209. {
  1210. if (branch->getTornOff())
  1211. {
  1212. ((LLFloater*)branch->getParent())->setFocus(FALSE);
  1213. branch->clearHoverItem();
  1214. }
  1215. else
  1216. {
  1217. branch->setVisible( FALSE );
  1218. }
  1219. }
  1220. }
  1221. BOOL LLMenuItemBranchDownGL::isActive() const
  1222. {
  1223. // for top level menus, being open is sufficient to be considered 
  1224. // active, because clicking on them with the mouse will open
  1225. // them, without moving keyboard focus to them
  1226. return isOpen();
  1227. }
  1228. BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
  1229. {
  1230. // switch to mouse control mode
  1231. LLMenuGL::setKeyboardMode(FALSE);
  1232. onCommit();
  1233. make_ui_sound("UISndClick");
  1234. return TRUE;
  1235. }
  1236. BOOL LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask )
  1237. {
  1238. return TRUE;
  1239. }
  1240. BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
  1241. {
  1242. BOOL branch_visible = getBranch()->getVisible();
  1243. BOOL handled = getBranch()->handleAcceleratorKey(key, mask);
  1244. if (handled && !branch_visible && getVisible())
  1245. {
  1246. // flash this menu entry because we triggered an invisible menu item
  1247. LLMenuHolderGL::setActivatedItem(this);
  1248. }
  1249. return handled;
  1250. }
  1251. BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
  1252. {
  1253. BOOL menu_open = getBranch()->getVisible();
  1254. // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
  1255. if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode()))
  1256. {
  1257. if (key == KEY_LEFT)
  1258. {
  1259. // switch to keyboard navigation mode
  1260. LLMenuGL::setKeyboardMode(TRUE);
  1261. LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
  1262. // open new menu only if previous menu was open
  1263. if (itemp && itemp->getEnabled() && menu_open)
  1264. {
  1265. itemp->onCommit();
  1266. }
  1267. return TRUE;
  1268. }
  1269. else if (key == KEY_RIGHT)
  1270. {
  1271. // switch to keyboard navigation mode
  1272. LLMenuGL::setKeyboardMode(TRUE);
  1273. LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
  1274. // open new menu only if previous menu was open
  1275. if (itemp && itemp->getEnabled() && menu_open)
  1276. {
  1277. itemp->onCommit();
  1278. }
  1279. return TRUE;
  1280. }
  1281. else if (key == KEY_DOWN)
  1282. {
  1283. // switch to keyboard navigation mode
  1284. LLMenuGL::setKeyboardMode(TRUE);
  1285. if (!isActive())
  1286. {
  1287. onCommit();
  1288. }
  1289. getBranch()->highlightNextItem(NULL);
  1290. return TRUE;
  1291. }
  1292. else if (key == KEY_UP)
  1293. {
  1294. // switch to keyboard navigation mode
  1295. LLMenuGL::setKeyboardMode(TRUE);
  1296. if (!isActive())
  1297. {
  1298. onCommit();
  1299. }
  1300. getBranch()->highlightPrevItem(NULL);
  1301. return TRUE;
  1302. }
  1303. }
  1304. return FALSE;
  1305. }
  1306. void LLMenuItemBranchDownGL::draw( void )
  1307. {
  1308. //FIXME: try removing this
  1309. if (getBranch()->getVisible() && !getBranch()->getTornOff())
  1310. {
  1311. setHighlight(TRUE);
  1312. }
  1313. if( getHighlight() )
  1314. {
  1315. gGL.color4fv( mHighlightBackground.get().mV );
  1316. gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
  1317. }
  1318. LLColor4 color;
  1319. if (getHighlight())
  1320. {
  1321. color = mHighlightForeground.get();
  1322. }
  1323. else if( getEnabled() )
  1324. {
  1325. color = mEnabledColor.get();
  1326. }
  1327. else
  1328. {
  1329. color = mDisabledColor.get();
  1330. }
  1331. getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
  1332.    LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL);
  1333. // underline navigation key only when keyboard navigation has been initiated
  1334. if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
  1335. {
  1336. std::string upper_case_label = mLabel.getString();
  1337. LLStringUtil::toUpper(upper_case_label);
  1338. std::string::size_type offset = upper_case_label.find(getJumpKey());
  1339. if (offset != std::string::npos)
  1340. {
  1341. S32 x_offset = llround((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
  1342. S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset);
  1343. S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1);
  1344. gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);
  1345. }
  1346. }
  1347. // reset every frame so that we only show highlight 
  1348. // when we get hover events on that frame
  1349. setHover(FALSE);
  1350. }
  1351. class LLMenuScrollItem : public LLMenuItemCallGL
  1352. {
  1353. public:
  1354. enum EArrowType
  1355. {
  1356. ARROW_DOWN,
  1357. ARROW_UP
  1358. };
  1359. struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes>
  1360. {
  1361. static void declareValues()
  1362. {
  1363. declare("up", ARROW_UP);
  1364. declare("down", ARROW_DOWN);
  1365. }
  1366. };
  1367. struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
  1368. {
  1369. Optional<EArrowType, ArrowTypes> arrow_type;
  1370. Optional<CommitCallbackParam> scroll_callback;
  1371. };
  1372. protected:
  1373. LLMenuScrollItem(const Params&);
  1374. friend class LLUICtrlFactory;
  1375. public:
  1376. /*virtual*/ void draw();
  1377. /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
  1378. /*virtual*/ void setEnabled(BOOL enabled);
  1379. virtual void onCommit( void );
  1380. private:
  1381. LLButton* mArrowBtn;
  1382. };
  1383. LLMenuScrollItem::LLMenuScrollItem(const Params& p)
  1384. : LLMenuItemCallGL(p)
  1385. {
  1386. std::string icon;
  1387. if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP)
  1388. {
  1389. icon = "arrow_up.tga";
  1390. }
  1391. else
  1392. {
  1393. icon = "arrow_down.tga";
  1394. }
  1395. LLButton::Params bparams;
  1396. bparams.mouse_opaque(true);
  1397. bparams.scale_image(false);
  1398. bparams.click_callback(p.scroll_callback);
  1399. bparams.mouse_held_callback(p.scroll_callback);
  1400. bparams.follows.flags(FOLLOWS_ALL);
  1401. std::string background = "transparent.j2c";
  1402. bparams.image_unselected.name(background);
  1403. bparams.image_disabled.name(background);
  1404. bparams.image_selected.name(background);
  1405. bparams.image_hover_selected.name(background);
  1406. bparams.image_disabled_selected.name(background);
  1407. bparams.image_hover_unselected.name(background);
  1408. bparams.image_overlay.name(icon);
  1409. mArrowBtn = LLUICtrlFactory::create<LLButton>(bparams);
  1410. addChild(mArrowBtn);
  1411. }
  1412. /*virtual*/
  1413. void LLMenuScrollItem::draw()
  1414. {
  1415. LLUICtrl::draw();
  1416. }
  1417. /*virtual*/
  1418. void LLMenuScrollItem::reshape(S32 width, S32 height, BOOL called_from_parent)
  1419. {
  1420. mArrowBtn->reshape(width, height, called_from_parent);
  1421. LLView::reshape(width, height, called_from_parent);
  1422. }
  1423. /*virtual*/
  1424. void LLMenuScrollItem::setEnabled(BOOL enabled)
  1425. {
  1426. mArrowBtn->setEnabled(enabled);
  1427. LLView::setEnabled(enabled);
  1428. }
  1429. void LLMenuScrollItem::onCommit( void )
  1430. {
  1431. LLUICtrl::onCommit();
  1432. }
  1433. ///============================================================================
  1434. /// Class LLMenuGL
  1435. ///============================================================================
  1436. LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
  1437. : LLUICtrl(p),
  1438. mBackgroundColor( p.bg_color() ),
  1439. mBgVisible( p.bg_visible ),
  1440. mDropShadowed( p.drop_shadow ),
  1441. mHasSelection(false),
  1442. mHorizontalLayout( p.horizontal_layout ),
  1443. mScrollable(mHorizontalLayout ? FALSE : p.scrollable), // Scrolling is supported only for vertical layout
  1444. mMaxScrollableItems(p.max_scrollable_items),
  1445. mPreferredWidth(p.preferred_width),
  1446. mKeepFixedSize( p.keep_fixed_size ),
  1447. mLabel (p.label),
  1448. mLastMouseX(0),
  1449. mLastMouseY(0),
  1450. mMouseVelX(0),
  1451. mMouseVelY(0),
  1452. mTornOff(FALSE),
  1453. mTearOffItem(NULL),
  1454. mSpilloverBranch(NULL),
  1455. mFirstVisibleItem(NULL),
  1456. mArrowUpItem(NULL),
  1457. mArrowDownItem(NULL),
  1458. mSpilloverMenu(NULL),
  1459. mJumpKey(p.jump_key),
  1460. mCreateJumpKeys(p.create_jump_keys),
  1461. mNeedsArrange(FALSE), 
  1462. mShortcutPad(p.shortcut_pad)
  1463. {
  1464. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  1465. boost::char_separator<char> sep("_");
  1466. tokenizer tokens(p.label(), sep);
  1467. tokenizer::iterator token_iter;
  1468. S32 token_count = 0;
  1469. std::string new_menu_label;
  1470. for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  1471. {
  1472. new_menu_label += (*token_iter);
  1473. if (token_count > 0)
  1474. {
  1475. setJumpKey((*token_iter).c_str()[0]);
  1476. }
  1477. ++token_count;
  1478. }
  1479. setLabel(new_menu_label);
  1480. mFadeTimer.stop();
  1481. }
  1482. void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
  1483. {
  1484. LLUICtrl::initFromParams(p);
  1485. setCanTearOff(p.can_tear_off);
  1486. }
  1487. // Destroys the object
  1488. LLMenuGL::~LLMenuGL( void )
  1489. {
  1490. // delete the branch, as it might not be in view hierarchy
  1491. // leave the menu, because it is always in view hierarchy
  1492. delete mSpilloverBranch;
  1493. mJumpKeys.clear();
  1494. }
  1495. void LLMenuGL::setCanTearOff(BOOL tear_off)
  1496. {
  1497. if (tear_off && mTearOffItem == NULL)
  1498. {
  1499. LLMenuItemTearOffGL::Params p;
  1500. mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
  1501. addChildInBack(mTearOffItem);
  1502. }
  1503. else if (!tear_off && mTearOffItem != NULL)
  1504. {
  1505. mItems.remove(mTearOffItem);
  1506. removeChild(mTearOffItem);
  1507. delete mTearOffItem;
  1508. mTearOffItem = NULL;
  1509. needsArrange();
  1510. }
  1511. }
  1512. bool LLMenuGL::addChild(LLView* view, S32 tab_group)
  1513. {
  1514. if (LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view))
  1515. {
  1516. appendMenu(menup);
  1517. return true;
  1518. }
  1519. else if (LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view))
  1520. {
  1521. append(itemp);
  1522. return true;
  1523. }
  1524. return false;
  1525. }
  1526. void LLMenuGL::removeChild( LLView* ctrl)
  1527. {
  1528. // previously a dynamic_cast with if statement to check validity
  1529. // unfortunately removeChild is called by ~LLView, and at that point the
  1530. // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail
  1531. LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl);
  1532. item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
  1533. if (found_it != mItems.end())
  1534. {
  1535. mItems.erase(found_it);
  1536. }
  1537. return LLUICtrl::removeChild(ctrl);
  1538. }
  1539. BOOL LLMenuGL::postBuild()
  1540. {
  1541. createJumpKeys();
  1542. return LLUICtrl::postBuild();
  1543. }
  1544. // are we the childmost active menu and hence our jump keys should be enabled?
  1545. // or are we a free-standing torn-off menu (which uses jump keys too)
  1546. BOOL LLMenuGL::jumpKeysActive()
  1547. {
  1548. LLMenuItemGL* highlighted_item = getHighlightedItem();
  1549. BOOL active = getVisible() && getEnabled();
  1550. if (getTornOff())
  1551. {
  1552. // activation of jump keys on torn off menus controlled by keyboard focus
  1553. active = active && ((LLFloater*)getParent())->hasFocus();
  1554. }
  1555. else
  1556. {
  1557. // Are we the terminal active menu?
  1558. // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus)
  1559. // and we don't have a highlighted menu item pointing to an active sub-menu
  1560. active = active && (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active...
  1561.                 && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active
  1562. }
  1563. return active;
  1564. }
  1565. BOOL LLMenuGL::isOpen()
  1566. {
  1567. if (getTornOff())
  1568. {
  1569. LLMenuItemGL* itemp = getHighlightedItem();
  1570. // if we have an open sub-menu, then we are considered part of 
  1571. // the open menu chain even if we don't have focus
  1572. if (itemp && itemp->isOpen())
  1573. {
  1574. return TRUE;
  1575. }
  1576. // otherwise we are only active if we have keyboard focus
  1577. return ((LLFloater*)getParent())->hasFocus();
  1578. }
  1579. else
  1580. {
  1581. // normally, menus are hidden as soon as the user focuses
  1582. // on another menu, so just use the visibility criterion
  1583. return getVisible();
  1584. }
  1585. }
  1586. void LLMenuGL::scrollItemsUp()
  1587. {
  1588. // Slowing down the items scrolling when arrow button is held 
  1589. if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem)
  1590. {
  1591. mScrollItemsTimer.setTimerExpirySec(.033f);
  1592. }
  1593. else
  1594. {
  1595. return;
  1596. }
  1597. item_list_t::iterator cur_item_iter;
  1598. item_list_t::iterator prev_item_iter;
  1599. for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
  1600. {
  1601. if( (*cur_item_iter) == mFirstVisibleItem)
  1602. {
  1603. break;
  1604. }
  1605. if ((*cur_item_iter)->getVisible())
  1606. {
  1607. prev_item_iter = cur_item_iter;
  1608. }
  1609. }
  1610. if ((*prev_item_iter)->getVisible())
  1611. {
  1612. mFirstVisibleItem = *prev_item_iter;
  1613. }
  1614. mNeedsArrange = TRUE;
  1615. arrangeAndClear();
  1616. }
  1617. void LLMenuGL::scrollItemsDown()
  1618. {
  1619. // Slowing down the items scrolling when arrow button is held 
  1620. if (mScrollItemsTimer.hasExpired())
  1621. {
  1622. mScrollItemsTimer.setTimerExpirySec(.033f);
  1623. }
  1624. else
  1625. {
  1626. return;
  1627. }
  1628. if (NULL == mFirstVisibleItem)
  1629. {
  1630. mFirstVisibleItem = *mItems.begin();
  1631. }
  1632. item_list_t::iterator cur_item_iter;
  1633. for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
  1634. {
  1635. if( (*cur_item_iter) == mFirstVisibleItem)
  1636. {
  1637. break;
  1638. }
  1639. }
  1640. item_list_t::iterator next_item_iter;
  1641. if (cur_item_iter != mItems.end())
  1642. {
  1643. for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
  1644. {
  1645. if( (*next_item_iter)->getVisible())
  1646. {
  1647. break;
  1648. }
  1649. }
  1650. if (next_item_iter != mItems.end() &&
  1651.     (*next_item_iter)->getVisible())
  1652. {
  1653. mFirstVisibleItem = *next_item_iter;
  1654. }
  1655. }
  1656. mNeedsArrange = TRUE;
  1657. arrangeAndClear();
  1658. }
  1659. // rearrange the child rects so they fit the shape of the menu.
  1660. void LLMenuGL::arrange( void )
  1661. {
  1662. // calculate the height & width, and set our rect based on that
  1663. // information.
  1664. const LLRect& initial_rect = getRect();
  1665. U32 width = 0, height = MENU_ITEM_PADDING;
  1666. cleanupSpilloverBranch();
  1667. if( mItems.size() ) 
  1668. {
  1669. const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0);
  1670. // torn off menus are not constrained to the size of the screen
  1671. U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth();
  1672. U32 max_height = U32_MAX;
  1673. if (!getTornOff())
  1674. {
  1675. max_height = getRect().mTop - menu_region_rect.mBottom;
  1676. if (menu_region_rect.mTop - getRect().mTop > (S32)max_height)
  1677. {
  1678. max_height = menu_region_rect.mTop - getRect().mTop;
  1679. }
  1680. }
  1681. // *FIX: create the item first and then ask for its dimensions?
  1682. S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate
  1683. S32 spillover_item_height = llround(LLFontGL::getFontSansSerif()->getLineHeight()) + MENU_ITEM_PADDING;
  1684. // Scrolling support
  1685. item_list_t::iterator first_visible_item_iter;
  1686. item_list_t::iterator first_hidden_item_iter = mItems.end();
  1687. S32 height_before_first_visible_item = -1;
  1688. S32 visible_items_height = 0;
  1689. U32 scrollable_items_cnt = 0;
  1690. if (mHorizontalLayout)
  1691. {
  1692. item_list_t::iterator item_iter;
  1693. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  1694. {
  1695. // do first so LLMenuGLItemCall can call on_visible to determine if visible
  1696. (*item_iter)->buildDrawLabel();
  1697. if ((*item_iter)->getVisible())
  1698. {
  1699. if (!getTornOff() 
  1700. && *item_iter != mSpilloverBranch
  1701. && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width)
  1702. {
  1703. // no room for any more items
  1704. createSpilloverBranch();
  1705. std::vector<LLMenuItemGL*> items_to_remove;
  1706. std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove));
  1707. std::vector<LLMenuItemGL*>::iterator spillover_iter;
  1708. for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter)
  1709. {
  1710. LLMenuItemGL* itemp = (*spillover_iter);
  1711. removeChild(itemp);
  1712. mSpilloverMenu->addChild(itemp);
  1713. }
  1714. addChild(mSpilloverBranch);
  1715. height = llmax(height, mSpilloverBranch->getNominalHeight());
  1716. width += mSpilloverBranch->getNominalWidth();
  1717. break;
  1718. }
  1719. else
  1720. {
  1721. // track our rect
  1722. height = llmax(height, (*item_iter)->getNominalHeight());
  1723. width += (*item_iter)->getNominalWidth();
  1724. }
  1725. }
  1726. }
  1727. }
  1728. else
  1729. {
  1730. item_list_t::iterator item_iter;
  1731. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  1732. {
  1733. // do first so LLMenuGLItemCall can call on_visible to determine if visible
  1734. (*item_iter)->buildDrawLabel();
  1735. if ((*item_iter)->getVisible())
  1736. {
  1737. if (!getTornOff() 
  1738. && !mScrollable
  1739. && *item_iter != mSpilloverBranch
  1740. && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
  1741. {
  1742. // no room for any more items
  1743. createSpilloverBranch();
  1744. std::vector<LLMenuItemGL*> items_to_remove;
  1745. std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove));
  1746. std::vector<LLMenuItemGL*>::iterator spillover_iter;
  1747. for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter)
  1748. {
  1749. LLMenuItemGL* itemp = (*spillover_iter);
  1750. removeChild(itemp);
  1751. mSpilloverMenu->addChild(itemp);
  1752. }
  1753. addChild(mSpilloverBranch);
  1754. height += mSpilloverBranch->getNominalHeight();
  1755. width = llmax( width, mSpilloverBranch->getNominalWidth() );
  1756. break;
  1757. }
  1758. else
  1759. {
  1760. // track our rect
  1761. height += (*item_iter)->getNominalHeight();
  1762. width = llmax( width, (*item_iter)->getNominalWidth() );
  1763. }
  1764. if (mScrollable)
  1765. {
  1766. // Determining visible items boundaries
  1767. if (NULL == mFirstVisibleItem)
  1768. {
  1769. mFirstVisibleItem = *item_iter;
  1770. }
  1771. if (*item_iter == mFirstVisibleItem)
  1772. {
  1773. height_before_first_visible_item = height - (*item_iter)->getNominalHeight();
  1774. first_visible_item_iter = item_iter;
  1775. scrollable_items_cnt = 0;
  1776. }
  1777. if (-1 != height_before_first_visible_item && 0 == visible_items_height &&
  1778.     (++scrollable_items_cnt > mMaxScrollableItems ||
  1779.      height - height_before_first_visible_item > max_height - spillover_item_height * 2 ))
  1780. {
  1781. first_hidden_item_iter = item_iter;
  1782. visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight();
  1783. scrollable_items_cnt--;
  1784. }
  1785. }
  1786. }
  1787. }
  1788. if (mPreferredWidth < U32_MAX)
  1789. width = llmin(mPreferredWidth, max_width);
  1790. if (mScrollable)
  1791. {
  1792. S32 max_items_height = max_height - spillover_item_height * 2;
  1793. if (visible_items_height == 0)
  1794. visible_items_height = height - height_before_first_visible_item;
  1795. // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit
  1796. if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems)
  1797. {
  1798. item_list_t::iterator tmp_iter(first_visible_item_iter);
  1799. while (visible_items_height < max_items_height &&
  1800.        scrollable_items_cnt < mMaxScrollableItems &&
  1801.        first_visible_item_iter != mItems.begin())
  1802. {
  1803. if ((*first_visible_item_iter)->getVisible())
  1804. {
  1805. // It keeps visible item, after first_visible_item_iter
  1806. tmp_iter = first_visible_item_iter;
  1807. }
  1808. first_visible_item_iter--;
  1809. if ((*first_visible_item_iter)->getVisible())
  1810. {
  1811. visible_items_height += (*first_visible_item_iter)->getNominalHeight();
  1812. height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight();
  1813. scrollable_items_cnt++;
  1814. }
  1815. }
  1816. // Roll back one item, that doesn't fit
  1817. if (visible_items_height > max_items_height)
  1818. {
  1819. visible_items_height -= (*first_visible_item_iter)->getNominalHeight();
  1820. height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight();
  1821. scrollable_items_cnt--;
  1822. first_visible_item_iter = tmp_iter;
  1823. }
  1824. if (!(*first_visible_item_iter)->getVisible())
  1825. {
  1826. first_visible_item_iter = tmp_iter;
  1827. }
  1828. mFirstVisibleItem = *first_visible_item_iter;
  1829. }
  1830. }
  1831. }
  1832. S32 cur_height = (S32)llmin(max_height, height);
  1833. if (mScrollable &&
  1834.     (height_before_first_visible_item > MENU_ITEM_PADDING ||
  1835.     height_before_first_visible_item + visible_items_height < (S32)height))
  1836. {
  1837. // Reserving 2 extra slots for arrow items
  1838. cur_height = visible_items_height + spillover_item_height * 2;
  1839. }
  1840. setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height));
  1841. S32 cur_width = 0;
  1842. S32 offset = 0;
  1843. if (mScrollable)
  1844. {
  1845. // No space for all items, creating arrow items
  1846. if (height_before_first_visible_item > MENU_ITEM_PADDING ||
  1847.     height_before_first_visible_item + visible_items_height < (S32)height)
  1848. {
  1849. if (NULL == mArrowUpItem)
  1850. {
  1851. LLMenuScrollItem::Params item_params;
  1852. item_params.name(ARROW_UP);
  1853. item_params.arrow_type(LLMenuScrollItem::ARROW_UP);
  1854. item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItemsUp, this));
  1855. mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
  1856. LLUICtrl::addChild(mArrowUpItem);
  1857. }
  1858. if (NULL == mArrowDownItem)
  1859. {
  1860. LLMenuScrollItem::Params item_params;
  1861. item_params.name(ARROW_DOWN);
  1862. item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN);
  1863. item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItemsDown, this));
  1864. mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
  1865. LLUICtrl::addChild(mArrowDownItem);
  1866. }
  1867. LLRect rect;
  1868. mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight()));
  1869. mArrowUpItem->setVisible(TRUE);
  1870. mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING);
  1871. mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight());
  1872. mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight()));
  1873. mArrowDownItem->setVisible(TRUE);
  1874. mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height);
  1875. mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight());
  1876. cur_height -= mArrowUpItem->getNominalHeight();
  1877. offset = menu_region_rect.mRight; // This moves items behind visible area
  1878. }
  1879. else
  1880. {
  1881. if (NULL != mArrowUpItem)
  1882. {
  1883. mArrowUpItem->setVisible(FALSE);
  1884. }
  1885. if (NULL != mArrowDownItem)
  1886. {
  1887. mArrowDownItem->setVisible(FALSE);
  1888. }
  1889. }
  1890. }
  1891. item_list_t::iterator item_iter;
  1892. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  1893. {
  1894. if ((*item_iter)->getVisible())
  1895. {
  1896. if (mScrollable)
  1897. {
  1898. if (item_iter == first_visible_item_iter)
  1899. {
  1900. offset = 0;
  1901. }
  1902. else if (item_iter == first_hidden_item_iter)
  1903. {
  1904. offset = menu_region_rect.mRight;  // This moves items behind visible area
  1905. }
  1906. }
  1907. // setup item rect to hold label
  1908. LLRect rect;
  1909. if (mHorizontalLayout)
  1910. {
  1911. rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height);
  1912. cur_width += (*item_iter)->getNominalWidth();
  1913. }
  1914. else
  1915. {
  1916. rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight());
  1917. if (offset == 0)
  1918. {
  1919. cur_height -= (*item_iter)->getNominalHeight();
  1920. }
  1921. }
  1922. (*item_iter)->setRect( rect );
  1923. }
  1924. }
  1925. }
  1926. if (mKeepFixedSize)
  1927. {
  1928. reshape(initial_rect.getWidth(), initial_rect.getHeight());
  1929. }
  1930. }
  1931. void LLMenuGL::arrangeAndClear( void )
  1932. {
  1933. if (mNeedsArrange)
  1934. {
  1935. arrange();
  1936. mNeedsArrange = FALSE;
  1937. }
  1938. }
  1939. void LLMenuGL::createSpilloverBranch()
  1940. {
  1941. if (!mSpilloverBranch)
  1942. {
  1943. // should be NULL but delete anyway
  1944. delete mSpilloverMenu;
  1945. // technically, you can't tear off spillover menus, but we're passing the handle
  1946. // along just to be safe
  1947. LLMenuGL::Params p;
  1948. p.name("More");
  1949. p.label("More"); // *TODO: Translate
  1950. p.bg_color(mBackgroundColor);
  1951. p.bg_visible(true);
  1952. p.can_tear_off(false);
  1953. mSpilloverMenu = new LLMenuGL(p);
  1954. mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer);
  1955. LLMenuItemBranchGL::Params branch_params;
  1956. branch_params.name = "More";
  1957. branch_params.label = "More"; // *TODO: Translate
  1958. branch_params.branch = mSpilloverMenu;
  1959. branch_params.font.style = "italic";
  1960. mSpilloverBranch = LLUICtrlFactory::create<LLMenuItemBranchGL>(branch_params);
  1961. }
  1962. }
  1963. void LLMenuGL::cleanupSpilloverBranch()
  1964. {
  1965. if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
  1966. {
  1967. // head-recursion to propagate items back up to root menu
  1968. mSpilloverMenu->cleanupSpilloverBranch();
  1969. // pop off spillover items
  1970. while (mSpilloverMenu->getItemCount())
  1971. {
  1972. LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
  1973. mSpilloverMenu->removeChild(itemp);
  1974. // put them at the end of our own list
  1975. addChild(itemp);
  1976. }
  1977. // Delete the branch, and since the branch will delete the menu,
  1978. // set the menu* to null.
  1979. delete mSpilloverBranch;
  1980. mSpilloverBranch = NULL;
  1981. mSpilloverMenu = NULL;
  1982. }
  1983. }
  1984. void LLMenuGL::createJumpKeys()
  1985. {
  1986. if (!mCreateJumpKeys) return;
  1987. mCreateJumpKeys = FALSE;
  1988. mJumpKeys.clear();
  1989. std::set<std::string> unique_words;
  1990. std::set<std::string> shared_words;
  1991. item_list_t::iterator item_it;
  1992. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  1993. boost::char_separator<char> sep(" ");
  1994. for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
  1995. {
  1996. std::string uppercase_label = (*item_it)->getLabel();
  1997. LLStringUtil::toUpper(uppercase_label);
  1998. tokenizer tokens(uppercase_label, sep);
  1999. tokenizer::iterator token_iter;
  2000. for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  2001. {
  2002. if (unique_words.find(*token_iter) != unique_words.end())
  2003. {
  2004. // this word exists in more than one menu instance
  2005. shared_words.insert(*token_iter);
  2006. }
  2007. else
  2008. {
  2009. // we have a new word, keep track of it
  2010. unique_words.insert(*token_iter);
  2011. }
  2012. }
  2013. }
  2014. // pre-assign specified jump keys
  2015. for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
  2016. {
  2017. KEY jump_key = (*item_it)->getJumpKey();
  2018. if(jump_key != KEY_NONE)
  2019. {
  2020. if (mJumpKeys.find(jump_key) == mJumpKeys.end())
  2021. {
  2022. mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
  2023. }
  2024. else
  2025. {
  2026. // this key is already spoken for, 
  2027. // so we need to reassign it below
  2028. (*item_it)->setJumpKey(KEY_NONE);
  2029. }
  2030. }
  2031. }
  2032. for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
  2033. {
  2034. // skip over items that already have assigned jump keys
  2035. if ((*item_it)->getJumpKey() != KEY_NONE)
  2036. {
  2037. continue;
  2038. }
  2039. std::string uppercase_label = (*item_it)->getLabel();
  2040. LLStringUtil::toUpper(uppercase_label);
  2041. tokenizer tokens(uppercase_label, sep);
  2042. tokenizer::iterator token_iter;
  2043. BOOL found_key = FALSE;
  2044. for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  2045. {
  2046. std::string uppercase_word = *token_iter;
  2047. // this word is not shared with other menu entries...
  2048. if (shared_words.find(*token_iter) == shared_words.end())
  2049. {
  2050. S32 i;
  2051. for(i = 0; i < (S32)uppercase_word.size(); i++)
  2052. {
  2053. char jump_key = uppercase_word[i];
  2054. if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) &&
  2055. mJumpKeys.find(jump_key) == mJumpKeys.end()))
  2056. {
  2057. mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
  2058. (*item_it)->setJumpKey(jump_key);
  2059. found_key = TRUE;
  2060. break;
  2061. }
  2062. }
  2063. }
  2064. if (found_key)
  2065. {
  2066. break;
  2067. }
  2068. }
  2069. }
  2070. }
  2071. // remove all items on the menu
  2072. void LLMenuGL::empty( void )
  2073. {
  2074. cleanupSpilloverBranch();
  2075. mItems.clear();
  2076. mFirstVisibleItem = NULL;
  2077. mArrowUpItem = NULL;
  2078. mArrowDownItem = NULL;
  2079. deleteAllChildren();
  2080. }
  2081. // Adjust rectangle of the menu
  2082. void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
  2083. {
  2084. setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom));
  2085. needsArrange();
  2086. }
  2087. BOOL LLMenuGL::handleJumpKey(KEY key)
  2088. {
  2089. // must perform case-insensitive comparison, so just switch to uppercase input key
  2090. key = toupper(key);
  2091. navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
  2092. if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
  2093. {
  2094. // switch to keyboard navigation mode
  2095. LLMenuGL::setKeyboardMode(TRUE);
  2096. // force highlight to close old menus and open and sub-menus
  2097. found_it->second->setHighlight(TRUE);
  2098. found_it->second->onCommit();
  2099. }
  2100. // if we are navigating the menus, we need to eat the keystroke
  2101. // so rest of UI doesn't handle it
  2102. return TRUE;
  2103. }
  2104. // Add the menu item to this menu.
  2105. BOOL LLMenuGL::append( LLMenuItemGL* item )
  2106. {
  2107. if (!item) return FALSE;
  2108. mItems.push_back( item );
  2109. LLUICtrl::addChild(item);
  2110. needsArrange();
  2111. return TRUE;
  2112. }
  2113. // add a separator to this menu
  2114. BOOL LLMenuGL::addSeparator()
  2115. {
  2116. LLMenuItemGL* separator = new LLMenuItemSeparatorGL();
  2117. return addChild(separator);
  2118. }
  2119. // add a menu - this will create a cascading menu
  2120. BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
  2121. {
  2122. if( menu == this )
  2123. {
  2124. llerrs << "** Attempt to attach menu to itself. This is certainly "
  2125.    << "a logic error." << llendl;
  2126. }
  2127. BOOL success = TRUE;
  2128. LLMenuItemBranchGL::Params p;
  2129. p.name = menu->getName();
  2130. p.label = menu->getLabel();
  2131. p.branch = menu;
  2132. p.jump_key = menu->getJumpKey();
  2133. p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
  2134. p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
  2135. p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
  2136. p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
  2137. LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p);
  2138. success &= append( branch );
  2139. // Inherit colors
  2140. menu->setBackgroundColor( mBackgroundColor );
  2141. menu->updateParent(LLMenuGL::sMenuContainer);
  2142. return success;
  2143. }
  2144. void LLMenuGL::setEnabledSubMenus(BOOL enable)
  2145. {
  2146. setEnabled(enable);
  2147. item_list_t::iterator item_iter;
  2148. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2149. {
  2150. (*item_iter)->setEnabledSubMenus( enable );
  2151. }
  2152. }
  2153. // setItemEnabled() - pass the label and the enable flag for a menu
  2154. // item. TRUE will make sure it's enabled, FALSE will disable it.
  2155. void LLMenuGL::setItemEnabled( const std::string& name, BOOL enable )
  2156. {
  2157. item_list_t::iterator item_iter;
  2158. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2159. {
  2160. if( (*item_iter)->getName() == name )
  2161. {
  2162. (*item_iter)->setEnabled( enable );
  2163. (*item_iter)->setEnabledSubMenus( enable );
  2164. break;
  2165. }
  2166. }
  2167. }
  2168. void LLMenuGL::setItemVisible( const std::string& name, BOOL visible )
  2169. {
  2170. item_list_t::iterator item_iter;
  2171. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2172. {
  2173. if( (*item_iter)->getName() == name )
  2174. {
  2175. (*item_iter)->setVisible( visible );
  2176. needsArrange();
  2177. break;
  2178. }
  2179. }
  2180. }
  2181. void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
  2182. {
  2183. if (getVisible())
  2184. {
  2185. LLMenuHolderGL::setActivatedItem(item);
  2186. }
  2187. // update enabled and checkmark status
  2188. item->buildDrawLabel();
  2189. }
  2190. //  Set whether drop shadowed 
  2191. void LLMenuGL::setDropShadowed( const BOOL shadowed )
  2192. {
  2193. mDropShadowed = shadowed;
  2194. }
  2195. void LLMenuGL::setTornOff(BOOL torn_off)
  2196. mTornOff = torn_off;
  2197. }
  2198. U32 LLMenuGL::getItemCount()
  2199. {
  2200. return mItems.size();
  2201. }
  2202. LLMenuItemGL* LLMenuGL::getItem(S32 number)
  2203. {
  2204. if (number >= 0 && number < (S32)mItems.size())
  2205. {
  2206. item_list_t::iterator item_iter;
  2207. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2208. {
  2209. if (number == 0)
  2210. {
  2211. return (*item_iter);
  2212. }
  2213. number--;
  2214. }
  2215. }
  2216. return NULL;
  2217. }
  2218. LLMenuItemGL* LLMenuGL::getHighlightedItem()
  2219. {
  2220. item_list_t::iterator item_iter;
  2221. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2222. {
  2223. if ((*item_iter)->getHighlight())
  2224. {
  2225. return (*item_iter);
  2226. }
  2227. }
  2228. return NULL;
  2229. }
  2230. LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
  2231. {
  2232. // highlighting first item on a torn off menu is the
  2233. // same as giving focus to it
  2234. if (!cur_item && getTornOff())
  2235. {
  2236. ((LLFloater*)getParent())->setFocus(TRUE);
  2237. }
  2238. item_list_t::iterator cur_item_iter;
  2239. for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter)
  2240. {
  2241. if( (*cur_item_iter) == cur_item)
  2242. {
  2243. break;
  2244. }
  2245. }
  2246. item_list_t::iterator next_item_iter;
  2247. if (cur_item_iter == mItems.end())
  2248. {
  2249. next_item_iter = mItems.begin();
  2250. }
  2251. else
  2252. {
  2253. next_item_iter = cur_item_iter;
  2254. next_item_iter++;
  2255. if (next_item_iter == mItems.end())
  2256. {
  2257. next_item_iter = mItems.begin();
  2258. }
  2259. }
  2260. // when first highlighting a menu, skip over tear off menu item
  2261. if (mTearOffItem && !cur_item)
  2262. {
  2263. // we know the first item is the tear off menu item
  2264. cur_item_iter = mItems.begin();
  2265. next_item_iter++;
  2266. if (next_item_iter == mItems.end())
  2267. {
  2268. next_item_iter = mItems.begin();
  2269. }
  2270. }
  2271. while(1)
  2272. {
  2273. // skip separators and disabled/invisible items
  2274. if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast<LLMenuItemSeparatorGL*>(*next_item_iter))
  2275. {
  2276. if (cur_item)
  2277. {
  2278. cur_item->setHighlight(FALSE);
  2279. }
  2280. (*next_item_iter)->setHighlight(TRUE);
  2281. return (*next_item_iter);
  2282. }
  2283. if (!skip_disabled || next_item_iter == cur_item_iter)
  2284. {
  2285. break;
  2286. }
  2287. next_item_iter++;
  2288. if (next_item_iter == mItems.end())
  2289. {
  2290. if (cur_item_iter == mItems.end())
  2291. {
  2292. break;
  2293. }
  2294. next_item_iter = mItems.begin();
  2295. }
  2296. }
  2297. return NULL;
  2298. }
  2299. LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
  2300. {
  2301. // highlighting first item on a torn off menu is the
  2302. // same as giving focus to it
  2303. if (!cur_item && getTornOff())
  2304. {
  2305. ((LLFloater*)getParent())->setFocus(TRUE);
  2306. }
  2307. item_list_t::reverse_iterator cur_item_iter;
  2308. for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter)
  2309. {
  2310. if( (*cur_item_iter) == cur_item)
  2311. {
  2312. break;
  2313. }
  2314. }
  2315. item_list_t::reverse_iterator prev_item_iter;
  2316. if (cur_item_iter == mItems.rend())
  2317. {
  2318. prev_item_iter = mItems.rbegin();
  2319. }
  2320. else
  2321. {
  2322. prev_item_iter = cur_item_iter;
  2323. prev_item_iter++;
  2324. if (prev_item_iter == mItems.rend())
  2325. {
  2326. prev_item_iter = mItems.rbegin();
  2327. }
  2328. }
  2329. while(1)
  2330. {
  2331. // skip separators and disabled/invisible items
  2332. if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME)
  2333. {
  2334. (*prev_item_iter)->setHighlight(TRUE);
  2335. return (*prev_item_iter);
  2336. }
  2337. if (!skip_disabled || prev_item_iter == cur_item_iter)
  2338. {
  2339. break;
  2340. }
  2341. prev_item_iter++;
  2342. if (prev_item_iter == mItems.rend())
  2343. {
  2344. if (cur_item_iter == mItems.rend())
  2345. {
  2346. break;
  2347. }
  2348. prev_item_iter = mItems.rbegin();
  2349. }
  2350. }
  2351. return NULL;
  2352. }
  2353. void LLMenuGL::buildDrawLabels()
  2354. {
  2355. item_list_t::iterator item_iter;
  2356. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2357. {
  2358. (*item_iter)->buildDrawLabel();
  2359. }
  2360. }
  2361. void LLMenuGL::updateParent(LLView* parentp)
  2362. {
  2363. if (getParent())
  2364. {
  2365. getParent()->removeChild(this);
  2366. }
  2367. if (parentp)
  2368. {
  2369. parentp->addChild(this);
  2370. }
  2371. item_list_t::iterator item_iter;
  2372. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2373. {
  2374. (*item_iter)->updateBranchParent(parentp);
  2375. }
  2376. }
  2377. BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
  2378. {
  2379. // don't handle if not enabled
  2380. if(!getEnabled())
  2381. {
  2382. return FALSE;
  2383. }
  2384. // Pass down even if not visible
  2385. item_list_t::iterator item_iter;
  2386. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2387. {
  2388. LLMenuItemGL* itemp = *item_iter;
  2389. if (itemp->handleAcceleratorKey(key, mask))
  2390. {
  2391. return TRUE;
  2392. }
  2393. }
  2394. return FALSE;
  2395. }
  2396. BOOL LLMenuGL::handleUnicodeCharHere( llwchar uni_char )
  2397. {
  2398. if (jumpKeysActive())
  2399. {
  2400. return handleJumpKey((KEY)uni_char);
  2401. }
  2402. return FALSE;
  2403. }
  2404. BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
  2405. {
  2406. // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
  2407. BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
  2408. S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
  2409. S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
  2410. LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
  2411. mouse_dir.normVec();
  2412. LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY);
  2413. mouse_avg_dir.normVec();
  2414. F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f));
  2415. mMouseVelX = llround(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp));
  2416. mMouseVelY = llround(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp));
  2417. mLastMouseX = x;
  2418. mLastMouseY = y;
  2419. // don't change menu focus unless mouse is moving or alt key is not held down
  2420. if ((llabs(mMouseVelX) > 0 || 
  2421. llabs(mMouseVelY) > 0) &&
  2422. (!mHasSelection ||
  2423. //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
  2424. (mMouseVelX < 0) ||
  2425. llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
  2426. {
  2427. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2428. {
  2429. LLView* viewp = *child_it;
  2430. S32 local_x = x - viewp->getRect().mLeft;
  2431. S32 local_y = y - viewp->getRect().mBottom;
  2432. if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
  2433. {
  2434. // moving mouse always highlights new item
  2435. if (mouse_delta_x != 0 || mouse_delta_y != 0)
  2436. {
  2437. ((LLMenuItemGL*)viewp)->setHighlight(FALSE);
  2438. }
  2439. }
  2440. }
  2441. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2442. {
  2443. LLView* viewp = *child_it;
  2444. S32 local_x = x - viewp->getRect().mLeft;
  2445. S32 local_y = y - viewp->getRect().mBottom;
  2446. //RN: always call handleHover to track mGotHover status
  2447. // but only set highlight when mouse is moving
  2448. if( viewp->getVisible() && 
  2449. //RN: allow disabled items to be highlighted to preserve "active" menus when
  2450. // moving mouse through them
  2451. //viewp->getEnabled() && 
  2452. viewp->pointInView(local_x, local_y) && 
  2453. viewp->handleHover(local_x, local_y, mask))
  2454. {
  2455. // moving mouse always highlights new item
  2456. if (mouse_delta_x != 0 || mouse_delta_y != 0)
  2457. {
  2458. ((LLMenuItemGL*)viewp)->setHighlight(TRUE);
  2459. LLMenuGL::setKeyboardMode(FALSE);
  2460. }
  2461. mHasSelection = true;
  2462. }
  2463. }
  2464. }
  2465. getWindow()->setCursor(UI_CURSOR_ARROW);
  2466. // *HACK Release the mouse capture
  2467. // This is done to release the mouse after the Navigation Bar "Back" or "Forward" button
  2468. // drop-down menu is shown. Otherwise any other view won't be able to handle mouse events
  2469. // until the user chooses one of the drop-down menu items.
  2470. return TRUE;
  2471. }
  2472. BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
  2473. {
  2474. if (!mScrollable)
  2475. return blockMouseEvent(x, y);
  2476. if( clicks > 0 )
  2477. {
  2478. while( clicks-- )
  2479. scrollItemsDown();
  2480. }
  2481. else
  2482. {
  2483. while( clicks++ )
  2484. scrollItemsUp();
  2485. }
  2486. return TRUE;
  2487. }
  2488. void LLMenuGL::draw( void )
  2489. {
  2490. if (mNeedsArrange)
  2491. {
  2492. arrange();
  2493. mNeedsArrange = FALSE;
  2494. }
  2495. if (mDropShadowed && !mTornOff)
  2496. {
  2497. static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0);
  2498. static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
  2499. gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, 
  2500. color_drop_shadow, drop_shadow_floater );
  2501. }
  2502. if( mBgVisible )
  2503. {
  2504. gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() );
  2505. }
  2506. LLView::draw();
  2507. }
  2508. void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha)
  2509. {
  2510. LLColor4 color = itemp->getHighlightBgColor() % alpha;
  2511. gGL.color4fv( color.mV );
  2512. LLRect item_rect = itemp->getRect();
  2513. gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0);
  2514. }
  2515. void LLMenuGL::setVisible(BOOL visible)
  2516. {
  2517. if (visible != getVisible())
  2518. {
  2519. if (!visible)
  2520. {
  2521. mFadeTimer.start();
  2522. clearHoverItem();
  2523. // reset last known mouse coordinates so
  2524. // we don't spoof a mouse move next time we're opened
  2525. mLastMouseX = 0;
  2526. mLastMouseY = 0;
  2527. }
  2528. else
  2529. {
  2530. mHasSelection = true;
  2531. mFadeTimer.stop();
  2532. }
  2533. LLView::setVisible(visible);
  2534. }
  2535. }
  2536. LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) const
  2537. {
  2538. LLView* view = findChildView(name, recurse);
  2539. if (view)
  2540. {
  2541. LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
  2542. if (branch)
  2543. {
  2544. return branch->getBranch();
  2545. }
  2546. LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
  2547. if (menup)
  2548. {
  2549. return menup;
  2550. }
  2551. }
  2552. llwarns << "Child Menu " << name << " not found in menu " << getName() << llendl;
  2553. return NULL;
  2554. }
  2555. BOOL LLMenuGL::clearHoverItem()
  2556. {
  2557. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2558. {
  2559. LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
  2560. if (itemp->getHighlight())
  2561. {
  2562. itemp->setHighlight(FALSE);
  2563. return TRUE;
  2564. }
  2565. }
  2566. return FALSE;
  2567. }
  2568. void hide_top_view( LLView* view )
  2569. {
  2570. if( view ) view->setVisible( FALSE );
  2571. }
  2572. // x and y are the desired location for the popup, in the spawning_view's
  2573. // coordinate frame, NOT necessarily the mouse location
  2574. // static
  2575. void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
  2576. {
  2577. const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
  2578. const S32 CURSOR_WIDTH = 12;
  2579. // Save click point for detecting cursor moves before mouse-up.
  2580. // Must be in local coords to compare with mouseUp events.
  2581. // If the mouse doesn't move, the menu will stay open ala the Mac.
  2582. // See also LLContextMenu::show()
  2583. S32 mouse_x, mouse_y;
  2584. // Resetting scrolling position
  2585. if (menu->isScrollable())
  2586. {
  2587. menu->mFirstVisibleItem = NULL;
  2588. }
  2589. menu->setVisible( TRUE );
  2590. // Fix menu rect if needed.
  2591. menu->needsArrange();
  2592. menu->arrangeAndClear();
  2593. LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y);
  2594. LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
  2595. const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect();
  2596. const S32 HPAD = 2;
  2597. LLRect rect = menu->getRect();
  2598. S32 left = x + HPAD;
  2599. S32 top = y;
  2600. spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
  2601. rect.setLeftTopAndSize( left, top,
  2602. rect.getWidth(), rect.getHeight() );
  2603. menu->setRect( rect );
  2604. // Adjust context menu to fit onscreen
  2605. LLRect mouse_rect;
  2606. const S32 MOUSE_CURSOR_PADDING = 5;
  2607. mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING, 
  2608. mouse_y + MOUSE_CURSOR_PADDING, 
  2609. CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, 
  2610. CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
  2611. menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE );
  2612. menu->getParent()->sendChildToFront(menu);
  2613. }
  2614. ///============================================================================
  2615. /// Class LLMenuBarGL
  2616. ///============================================================================
  2617. static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar");
  2618. LLMenuBarGL::LLMenuBarGL( const Params& p )
  2619. : LLMenuGL(p),
  2620. mAltKeyTrigger(FALSE)
  2621. {}
  2622. // Default destructor
  2623. LLMenuBarGL::~LLMenuBarGL()
  2624. {
  2625. std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
  2626. mAccelerators.clear();
  2627. }
  2628. BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
  2629. {
  2630. if (getHighlightedItem() && mask == MASK_NONE)
  2631. {
  2632. // unmodified key accelerators are ignored when navigating menu
  2633. // (but are used as jump keys so will still work when appropriate menu is up)
  2634. return FALSE;
  2635. }
  2636. BOOL result = LLMenuGL::handleAcceleratorKey(key, mask);
  2637. if (result && mask & MASK_ALT)
  2638. {
  2639. // ALT key used to trigger hotkey, don't use as shortcut to open menu
  2640. mAltKeyTrigger = FALSE;
  2641. }
  2642. if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key))
  2643. {
  2644. if (getHighlightedItem())
  2645. {
  2646. clearHoverItem();
  2647. }
  2648. else
  2649. {
  2650. // close menus originating from other menu bars when first opening menu via keyboard
  2651. LLMenuGL::sMenuContainer->hideMenus();
  2652. highlightNextItem(NULL);
  2653. LLMenuGL::setKeyboardMode(TRUE);
  2654. }
  2655. return TRUE;
  2656. }
  2657. return result;
  2658. }
  2659. BOOL LLMenuBarGL::handleKeyHere(KEY key, MASK mask)
  2660. {
  2661. static LLUICachedControl<bool> use_altkey_for_menus ("UseAltKeyForMenus", 0);
  2662. if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus)
  2663. {
  2664. mAltKeyTrigger = TRUE;
  2665. }
  2666. else // if any key other than ALT hit, clear out waiting for Alt key mode
  2667. {
  2668. mAltKeyTrigger = FALSE;
  2669. }
  2670. if (key == KEY_ESCAPE && mask == MASK_NONE)
  2671. {
  2672. LLMenuGL::setKeyboardMode(FALSE);
  2673. // if any menus are visible, this will return TRUE, stopping further processing of ESCAPE key
  2674. return LLMenuGL::sMenuContainer->hideMenus();
  2675. }
  2676. // before processing any other key, check to see if ALT key has triggered menu access
  2677. checkMenuTrigger();
  2678. return LLMenuGL::handleKeyHere(key, mask);
  2679. }
  2680. BOOL LLMenuBarGL::handleJumpKey(KEY key)
  2681. {
  2682. // perform case-insensitive comparison
  2683. key = toupper(key);
  2684. navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
  2685. if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
  2686. {
  2687. // switch to keyboard navigation mode
  2688. LLMenuGL::setKeyboardMode(TRUE);
  2689. found_it->second->setHighlight(TRUE);
  2690. found_it->second->onCommit();
  2691. }
  2692. return TRUE;
  2693. }
  2694. BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)
  2695. {
  2696. // clicks on menu bar closes existing menus from other contexts but leave
  2697. // own menu open so that we get toggle behavior
  2698. if (!getHighlightedItem() || !getHighlightedItem()->isActive())
  2699. {
  2700. LLMenuGL::sMenuContainer->hideMenus();
  2701. }
  2702. return LLMenuGL::handleMouseDown(x, y, mask);
  2703. }
  2704. void LLMenuBarGL::draw()
  2705. {
  2706. LLMenuItemGL* itemp = getHighlightedItem();
  2707. // If we are in mouse-control mode and the mouse cursor is not hovering over
  2708. // the current highlighted menu item and it isn't open, then remove the
  2709. // highlight. This is done via a polling mechanism here, as we don't receive
  2710.     // notifications when the mouse cursor moves off of us
  2711. if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode())
  2712. {
  2713. clearHoverItem();
  2714. }
  2715. checkMenuTrigger();
  2716. LLMenuGL::draw();
  2717. }
  2718. void LLMenuBarGL::checkMenuTrigger()
  2719. {
  2720. // has the ALT key been pressed and subsequently released?
  2721. if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT))
  2722. {
  2723. // if alt key was released quickly, treat it as a menu access key
  2724. // otherwise it was probably an Alt-zoom or similar action
  2725. static LLUICachedControl<F32> menu_access_key_time ("MenuAccessKeyTime", 0);
  2726. if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time ||
  2727. gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2)
  2728. {
  2729. if (getHighlightedItem())
  2730. {
  2731. clearHoverItem();
  2732. }
  2733. else
  2734. {
  2735. // close menus originating from other menu bars
  2736. LLMenuGL::sMenuContainer->hideMenus();
  2737. highlightNextItem(NULL);
  2738. LLMenuGL::setKeyboardMode(TRUE);
  2739. }
  2740. }
  2741. mAltKeyTrigger = FALSE;
  2742. }
  2743. }
  2744. BOOL LLMenuBarGL::jumpKeysActive()
  2745. {
  2746. // require user to be in keyboard navigation mode to activate key triggers
  2747. // as menu bars are always visible and it is easy to leave the mouse cursor over them
  2748. return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive();
  2749. }
  2750. // rearrange the child rects so they fit the shape of the menu bar.
  2751. void LLMenuBarGL::arrange( void )
  2752. {
  2753. U32 pos = 0;
  2754. LLRect rect( 0, getRect().getHeight(), 0, 0 );
  2755. item_list_t::const_iterator item_iter;
  2756. for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
  2757. {
  2758. LLMenuItemGL* item = *item_iter;
  2759. if (item->getVisible())
  2760. {
  2761. rect.mLeft = pos;
  2762. pos += item->getNominalWidth();
  2763. rect.mRight = pos;
  2764. item->setRect( rect );
  2765. item->buildDrawLabel();
  2766. }
  2767. }
  2768. reshape(rect.mRight, rect.getHeight());
  2769. }
  2770. S32 LLMenuBarGL::getRightmostMenuEdge()
  2771. {
  2772. // Find the last visible menu
  2773. item_list_t::reverse_iterator item_iter;
  2774. for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter)
  2775. {
  2776. if ((*item_iter)->getVisible())
  2777. {
  2778. break;
  2779. }
  2780. }
  2781. if (item_iter == mItems.rend())
  2782. {
  2783. return 0;
  2784. }
  2785. return (*item_iter)->getRect().mRight;
  2786. }
  2787. // add a vertical separator to this menu
  2788. BOOL LLMenuBarGL::addSeparator()
  2789. {
  2790. LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
  2791. return append( separator );
  2792. }
  2793. // add a menu - this will create a drop down menu.
  2794. BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu )
  2795. {
  2796. if( menu == this )
  2797. {
  2798. llerrs << "** Attempt to attach menu to itself. This is certainly "
  2799.    << "a logic error." << llendl;
  2800. }
  2801. BOOL success = TRUE;
  2802. // *TODO: Hack! Fix this
  2803. LLMenuItemBranchDownGL::Params p;
  2804. p.name = menu->getName();
  2805. p.label = menu->getLabel();
  2806. p.visible = menu->getVisible();
  2807. p.branch = menu;
  2808. p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
  2809. p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
  2810. p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
  2811. p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
  2812. LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p);
  2813. success &= branch->addToAcceleratorList(&mAccelerators);
  2814. success &= append( branch );
  2815. branch->setJumpKey(branch->getJumpKey());
  2816. menu->updateParent(LLMenuGL::sMenuContainer);
  2817. return success;
  2818. }
  2819. BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
  2820. {
  2821. BOOL handled = FALSE;
  2822. LLView* active_menu = NULL;
  2823. BOOL no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
  2824. S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
  2825. S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
  2826. mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
  2827. mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
  2828. mLastMouseX = x;
  2829. mLastMouseY = y;
  2830. // if nothing currently selected or mouse has moved since last call, pick menu item via mouse
  2831. // otherwise let keyboard control it
  2832. if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)
  2833. {
  2834. // find current active menu
  2835. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2836. {
  2837. LLView* viewp = *child_it;
  2838. if (((LLMenuItemGL*)viewp)->isOpen())
  2839. {
  2840. active_menu = viewp;
  2841. }
  2842. }
  2843. // check for new active menu
  2844. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2845. {
  2846. LLView* viewp = *child_it;
  2847. S32 local_x = x - viewp->getRect().mLeft;
  2848. S32 local_y = y - viewp->getRect().mBottom;
  2849. if( viewp->getVisible() && 
  2850. viewp->getEnabled() &&
  2851. viewp->pointInView(local_x, local_y) && 
  2852. viewp->handleHover(local_x, local_y, mask))
  2853. {
  2854. ((LLMenuItemGL*)viewp)->setHighlight(TRUE);
  2855. handled = TRUE;
  2856. if (active_menu && active_menu != viewp)
  2857. {
  2858. ((LLMenuItemGL*)viewp)->onCommit();
  2859. LLMenuGL::setKeyboardMode(FALSE);
  2860. }
  2861. LLMenuGL::setKeyboardMode(FALSE);
  2862. }
  2863. }
  2864. if (handled)
  2865. {
  2866. // set hover false on inactive menus
  2867. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2868. {
  2869. LLView* viewp = *child_it;
  2870. S32 local_x = x - viewp->getRect().mLeft;
  2871. S32 local_y = y - viewp->getRect().mBottom;
  2872. if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
  2873. {
  2874. ((LLMenuItemGL*)viewp)->setHighlight(FALSE);
  2875. }
  2876. }
  2877. }
  2878. }
  2879. getWindow()->setCursor(UI_CURSOR_ARROW);
  2880. return TRUE;
  2881. }
  2882. ///============================================================================
  2883. /// Class LLMenuHolderGL
  2884. ///============================================================================
  2885. LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX);
  2886. LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p)
  2887. : LLPanel(p)
  2888. {
  2889. sItemActivationTimer.stop();
  2890. mCanHide = TRUE;
  2891. }
  2892. void LLMenuHolderGL::draw()
  2893. {
  2894. LLView::draw();
  2895. // now draw last selected item as overlay
  2896. LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
  2897. if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
  2898. {
  2899. // make sure toggle items, for example, show the proper state when fading out
  2900. selecteditem->buildDrawLabel();
  2901. LLRect item_rect;
  2902. selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this);
  2903. F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME;
  2904. LLUI::pushMatrix();
  2905. {
  2906. LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom, 0.f);
  2907. selecteditem->getMenu()->drawBackground(selecteditem, interpolant);
  2908. selecteditem->draw();
  2909. }
  2910. LLUI::popMatrix();
  2911. }
  2912. }
  2913. BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )
  2914. {
  2915. BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
  2916. if (!handled)
  2917. {
  2918. // clicked off of menu, hide them all
  2919. hideMenus();
  2920. }
  2921. return handled;
  2922. }
  2923. BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
  2924. {
  2925. BOOL handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
  2926. if (!handled)
  2927. {
  2928. // clicked off of menu, hide them all
  2929. hideMenus();
  2930. }
  2931. return handled;
  2932. }
  2933. // This occurs when you mouse-down to spawn a context menu, hold the button 
  2934. // down, move off the menu, then mouse-up.  We want this to close the menu.
  2935. BOOL LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask )
  2936. {
  2937. const S32 SLOP = 2;
  2938. S32 spawn_dx = (x - sContextMenuSpawnPos.mX);
  2939. S32 spawn_dy = (y - sContextMenuSpawnPos.mY);
  2940. if (-SLOP <= spawn_dx && spawn_dx <= SLOP
  2941. && -SLOP <= spawn_dy && spawn_dy <= SLOP)
  2942. {
  2943. // we're still inside the slop region from spawning this menu
  2944. // so interpret the mouse-up as a single-click to show and leave on
  2945. // screen
  2946. sContextMenuSpawnPos.set(S32_MAX, S32_MAX);
  2947. return TRUE;
  2948. }
  2949. BOOL handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL;
  2950. if (!handled)
  2951. {
  2952. // clicked off of menu, hide them all
  2953. hideMenus();
  2954. }
  2955. return handled;
  2956. }
  2957. BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
  2958. {
  2959. BOOL handled =  false;
  2960. LLMenuGL* const  pMenu  = dynamic_cast<LLMenuGL*>(getVisibleMenu());
  2961. if (pMenu)
  2962. {
  2963. //handle ESCAPE and RETURN key
  2964. handled = LLPanel::handleKey(key, mask, called_from_parent);
  2965. if (!handled)
  2966. {
  2967. if (pMenu->getHighlightedItem())
  2968. {
  2969. handled = pMenu->handleKey(key, mask, TRUE);
  2970. }
  2971. else
  2972. {
  2973. //highlight first enabled one
  2974. pMenu->highlightNextItem(NULL);
  2975. handled = true;
  2976. }
  2977. }
  2978. }
  2979. return handled;
  2980. }
  2981. void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent)
  2982. {
  2983. if (width != getRect().getWidth() || height != getRect().getHeight())
  2984. {
  2985. hideMenus();
  2986. }
  2987. LLView::reshape(width, height, called_from_parent);
  2988. }
  2989. LLView* const LLMenuHolderGL::getVisibleMenu() const
  2990. {
  2991. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  2992. {
  2993. LLView* viewp = *child_it;
  2994. if (viewp->getVisible() && dynamic_cast<LLMenuBarGL*>(viewp) == NULL)
  2995. {
  2996. return viewp;
  2997. }
  2998. }
  2999. return NULL;
  3000. }
  3001. BOOL LLMenuHolderGL::hideMenus()
  3002. {
  3003. if (!mCanHide)
  3004. {
  3005. return FALSE;
  3006. }
  3007. BOOL menu_visible = hasVisibleMenu();
  3008. if (menu_visible)
  3009. {
  3010. LLMenuGL::setKeyboardMode(FALSE);
  3011. // clicked off of menu, hide them all
  3012. for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
  3013. {
  3014. LLView* viewp = *child_it;
  3015. // clicks off of menu do not hide menu bar
  3016. if (dynamic_cast<LLMenuBarGL*>(viewp) == NULL && viewp->getVisible())
  3017. {
  3018. viewp->setVisible(FALSE);
  3019. }
  3020. }
  3021. }
  3022. //if (gFocusMgr.childHasKeyboardFocus(this))
  3023. //{
  3024. // gFocusMgr.setKeyboardFocus(NULL);
  3025. //}
  3026. return menu_visible;
  3027. }
  3028. void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
  3029. {
  3030. sItemLastSelectedHandle = item->getHandle();
  3031. sItemActivationTimer.start();
  3032. }
  3033. ///============================================================================
  3034. /// Class LLTearOffMenu
  3035. ///============================================================================
  3036. LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : 
  3037. LLFloater(LLSD())
  3038. {
  3039. S32 floater_header_size = getHeaderHeight();
  3040. setName(menup->getName());
  3041. setTitle(menup->getLabel());
  3042. setCanMinimize(FALSE);
  3043. // flag menu as being torn off
  3044. menup->setTornOff(TRUE);
  3045. // update menu layout as torn off menu (no spillover menus)
  3046. menup->needsArrange();
  3047. LLRect rect;
  3048. menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
  3049. // make sure this floater is big enough for menu
  3050. mTargetHeight = (F32)(rect.getHeight() + floater_header_size);
  3051. reshape(rect.getWidth(), rect.getHeight());
  3052. setRect(rect);
  3053. // attach menu to floater
  3054. menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM );
  3055. mOldParent = menup->getParent();
  3056. addChild(menup);
  3057. menup->setVisible(TRUE);
  3058. LLRect menu_rect = menup->getRect();
  3059. menu_rect.setOriginAndSize( 1, 1,
  3060. menu_rect.getWidth(), menu_rect.getHeight());
  3061. menup->setRect(menu_rect);
  3062. menup->setDropShadowed(FALSE);
  3063. mMenu = menup;
  3064. // highlight first item (tear off item will be disabled)
  3065. mMenu->highlightNextItem(NULL);
  3066. // Can't do this in postBuild() because that is only called for floaters
  3067. // constructed from XML.
  3068. mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this));
  3069. }
  3070. LLTearOffMenu::~LLTearOffMenu()
  3071. {
  3072. }
  3073. void LLTearOffMenu::draw()
  3074. {
  3075. mMenu->setBackgroundVisible(isBackgroundOpaque());
  3076. mMenu->needsArrange();
  3077. if (getRect().getHeight() != mTargetHeight)
  3078. {
  3079. // animate towards target height
  3080. reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f))));
  3081. }
  3082. LLFloater::draw();
  3083. }
  3084. void LLTearOffMenu::onFocusReceived()
  3085. {
  3086. // if nothing is highlighted, just highlight first item
  3087. if (!mMenu->getHighlightedItem())
  3088. {
  3089. mMenu->highlightNextItem(NULL);
  3090. }
  3091. // parent menu items get highlights so navigation logic keeps working
  3092. LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
  3093. while(parent_menu_item)
  3094. {
  3095. if (parent_menu_item->getMenu()->getVisible())
  3096. {
  3097. parent_menu_item->setHighlight(TRUE);
  3098. parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem();
  3099. }
  3100. else
  3101. {
  3102. break;
  3103. }
  3104. }
  3105. LLFloater::onFocusReceived();
  3106. }
  3107. void LLTearOffMenu::onFocusLost()
  3108. {
  3109. // remove highlight from parent item and our own menu
  3110. mMenu->clearHoverItem();
  3111. LLFloater::onFocusLost();
  3112. }
  3113. BOOL LLTearOffMenu::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
  3114. {
  3115. // pass keystrokes down to menu
  3116. return mMenu->handleUnicodeChar(uni_char, TRUE);
  3117. }
  3118. BOOL LLTearOffMenu::handleKeyHere(KEY key, MASK mask)
  3119. {
  3120. if (!mMenu->getHighlightedItem())
  3121. {
  3122. if (key == KEY_UP)
  3123. {
  3124. mMenu->highlightPrevItem(NULL);
  3125. return TRUE;
  3126. }
  3127. else if (key == KEY_DOWN)
  3128. {
  3129. mMenu->highlightNextItem(NULL);
  3130. return TRUE;
  3131. }
  3132. }
  3133. // pass keystrokes down to menu
  3134. return mMenu->handleKey(key, mask, TRUE);
  3135. }
  3136. void LLTearOffMenu::translate(S32 x, S32 y)
  3137. {
  3138. if (x != 0 && y != 0)
  3139. {
  3140. // hide open sub-menus by clearing current hover item
  3141. mMenu->clearHoverItem();
  3142. }
  3143. LLFloater::translate(x, y);
  3144. }
  3145. //static
  3146. LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
  3147. {
  3148. LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
  3149. // keep onscreen
  3150. gFloaterView->adjustToFitScreen(tearoffp, FALSE);
  3151. tearoffp->openFloater(LLSD());
  3152. return tearoffp;
  3153. }
  3154. void LLTearOffMenu::closeTearOff()
  3155. {
  3156. removeChild(mMenu);
  3157. mOldParent->addChild(mMenu);
  3158. mMenu->clearHoverItem();
  3159. mMenu->setFollowsNone();
  3160. mMenu->setBackgroundVisible(TRUE);
  3161. mMenu->setVisible(FALSE);
  3162. mMenu->setTornOff(FALSE);
  3163. mMenu->setDropShadowed(TRUE);
  3164. }
  3165. //-----------------------------------------------------------------------------
  3166. // class LLContextMenuBranch
  3167. // A branch to another context menu
  3168. //-----------------------------------------------------------------------------
  3169. class LLContextMenuBranch : public LLMenuItemGL
  3170. {
  3171. public:
  3172. struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
  3173. {
  3174. Mandatory<LLContextMenu*> branch;
  3175. };
  3176. LLContextMenuBranch(const Params&);
  3177. virtual ~LLContextMenuBranch()
  3178. {
  3179. delete mBranch;
  3180. }
  3181. // called to rebuild the draw label
  3182. virtual void buildDrawLabel( void );
  3183. // onCommit() - do the primary funcationality of the menu item.
  3184. virtual void onCommit( void );
  3185. LLContextMenu* getBranch() { return mBranch; }
  3186. void setHighlight( BOOL highlight );
  3187. protected:
  3188. void showSubMenu();
  3189. LLContextMenu* mBranch;
  3190. };
  3191. LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) 
  3192. : LLMenuItemGL(p),
  3193. mBranch( p.branch )
  3194. {
  3195. mBranch->hide();
  3196. mBranch->setParentMenuItem(this);
  3197. }
  3198. // called to rebuild the draw label
  3199. void LLContextMenuBranch::buildDrawLabel( void )
  3200. {
  3201. {
  3202. // default enablement is this -- if any of the subitems are
  3203. // enabled, this item is enabled. JC
  3204. U32 sub_count = mBranch->getItemCount();
  3205. U32 i;
  3206. BOOL any_enabled = FALSE;
  3207. for (i = 0; i < sub_count; i++)
  3208. {
  3209. LLMenuItemGL* item = mBranch->getItem(i);
  3210. item->buildDrawLabel();
  3211. if (item->getEnabled() && !item->getDrawTextDisabled() )
  3212. {
  3213. any_enabled = TRUE;
  3214. break;
  3215. }
  3216. }
  3217. setDrawTextDisabled(!any_enabled);
  3218. setEnabled(TRUE);
  3219. }
  3220. mDrawAccelLabel.clear();
  3221. std::string st = mDrawAccelLabel;
  3222. appendAcceleratorString( st );
  3223. mDrawAccelLabel = st;
  3224. // No special branch suffix
  3225. mDrawBranchLabel.clear();
  3226. }
  3227. void LLContextMenuBranch::showSubMenu()
  3228. {
  3229. S32 center_x;
  3230. S32 center_y;
  3231. localPointToScreen(getRect().getWidth(), getRect().getHeight() , &center_x, &center_y);
  3232. mBranch->show( center_x, center_y);
  3233. }
  3234. // onCommit() - do the primary funcationality of the menu item.
  3235. void LLContextMenuBranch::onCommit( void )
  3236. {
  3237. showSubMenu();
  3238. }
  3239. void LLContextMenuBranch::setHighlight( BOOL highlight )
  3240. {
  3241. if (highlight == getHighlight()) return;
  3242. LLMenuItemGL::setHighlight(highlight);
  3243. if( highlight )
  3244. {
  3245. showSubMenu();
  3246. }
  3247. else
  3248. {
  3249. mBranch->hide();
  3250. }
  3251. }
  3252. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  3253. //-----------------------------------------------------------------------------
  3254. // class LLContextMenu
  3255. // A context menu
  3256. //-----------------------------------------------------------------------------
  3257. static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu");
  3258. static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu");
  3259. LLContextMenu::LLContextMenu(const Params& p)
  3260. : LLMenuGL(p),
  3261. mHoveredAnyItem(FALSE),
  3262. mHoverItem(NULL)
  3263. {
  3264. //setBackgroundVisible(TRUE);
  3265. }
  3266. void LLContextMenu::setVisible(BOOL visible)
  3267. {
  3268. if (!visible)
  3269. hide();
  3270. }
  3271. // Takes cursor position in screen space?
  3272. void LLContextMenu::show(S32 x, S32 y)
  3273. {
  3274. // Save click point for detecting cursor moves before mouse-up.
  3275. // Must be in local coords to compare with mouseUp events.
  3276. // If the mouse doesn't move, the menu will stay open ala the Mac.
  3277. // See also LLMenuGL::showPopup()
  3278. LLMenuHolderGL::sContextMenuSpawnPos.set(x,y);
  3279. arrangeAndClear();
  3280. S32 width = getRect().getWidth();
  3281. S32 height = getRect().getHeight();
  3282. const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
  3283. LLView* parent_view = getParent();
  3284. // Open upwards if menu extends past bottom
  3285. if (y - height < menu_region_rect.mBottom)
  3286. {
  3287. if (getParentMenuItem()) // Adjust if this is a submenu
  3288. {
  3289. y += height - getParentMenuItem()->getNominalHeight();
  3290. }
  3291. else
  3292. {
  3293. y += height;
  3294. }
  3295. }
  3296. // Open out to the left if menu extends past right edge
  3297. if (x + width > menu_region_rect.mRight)
  3298. {
  3299. if (getParentMenuItem())
  3300. {
  3301. x -= getParentMenuItem()->getRect().getWidth() + width;
  3302. }
  3303. else
  3304. {
  3305. x -= width;
  3306. }
  3307. }
  3308. S32 local_x, local_y;
  3309. parent_view->screenPointToLocal(x, y, &local_x, &local_y);
  3310. LLRect rect;
  3311. rect.setLeftTopAndSize(local_x, local_y, width, height);
  3312. setRect(rect);
  3313. arrange();
  3314. LLView::setVisible(TRUE);
  3315. }
  3316. void LLContextMenu::hide()
  3317. {
  3318. if (!getVisible()) return;
  3319. LLView::setVisible(FALSE);
  3320. if (mHoverItem)
  3321. {
  3322. mHoverItem->setHighlight( FALSE );
  3323. }
  3324. mHoverItem = NULL;
  3325. }
  3326. BOOL LLContextMenu::handleHover( S32 x, S32 y, MASK mask )
  3327. {
  3328. LLMenuGL::handleHover(x,y,mask);
  3329. BOOL handled = FALSE;
  3330. LLMenuItemGL *item = getHighlightedItem();
  3331. if (item && item->getEnabled())
  3332. {
  3333. getWindow()->setCursor(UI_CURSOR_ARROW);
  3334. handled = TRUE;
  3335. if (item != mHoverItem)
  3336. {
  3337. if (mHoverItem)
  3338. {
  3339. mHoverItem->setHighlight( FALSE );
  3340. }
  3341. mHoverItem = item;
  3342. mHoverItem->setHighlight( TRUE );
  3343. }
  3344. mHoveredAnyItem = TRUE;
  3345. }
  3346. else
  3347. {
  3348. // clear out our selection
  3349. if (mHoverItem)
  3350. {
  3351. mHoverItem->setHighlight(FALSE);
  3352. mHoverItem = NULL;
  3353. }
  3354. }
  3355. if( !handled && pointInView( x, y ) )
  3356. {
  3357. getWindow()->setCursor(UI_CURSOR_ARROW);
  3358. handled = TRUE;
  3359. }
  3360. return handled;
  3361. }
  3362. // handleMouseDown and handleMouseUp are handled by LLMenuGL
  3363. BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
  3364. {
  3365. BOOL handled = FALSE;
  3366. // The click was somewhere within our rectangle
  3367. LLMenuItemGL *item = getHighlightedItem();
  3368. S32 local_x = x - getRect().mLeft;
  3369. S32 local_y = y - getRect().mBottom;
  3370. BOOL clicked_in_menu = pointInView(local_x, local_y) ;
  3371. // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie
  3372. if (clicked_in_menu)
  3373. {
  3374. // capture mouse cursor as if on initial menu show
  3375. handled = TRUE;
  3376. }
  3377. if (item)
  3378. {
  3379. // lie to the item about where the click happened
  3380. // to make sure it's within the item's rectangle
  3381. if (item->handleMouseDown( 0, 0, mask ))
  3382. {
  3383. handled = TRUE;
  3384. }
  3385. }
  3386. return handled;
  3387. }
  3388. BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
  3389. {
  3390. S32 local_x = x - getRect().mLeft;
  3391. S32 local_y = y - getRect().mBottom;
  3392. if (!mHoveredAnyItem && !pointInView(local_x, local_y))
  3393. {
  3394. sMenuContainer->hideMenus();
  3395. return TRUE;
  3396. }
  3397. BOOL result = handleMouseUp( x, y, mask );
  3398. mHoveredAnyItem = FALSE;
  3399. return result;
  3400. }
  3401. void LLContextMenu::draw()
  3402. {
  3403. LLMenuGL::draw();
  3404. }
  3405. BOOL LLContextMenu::appendContextSubMenu(LLContextMenu *menu)
  3406. {
  3407. if (menu == this)
  3408. {
  3409. llerrs << "Can't attach a context menu to itself" << llendl;
  3410. }
  3411. LLContextMenuBranch *item;
  3412. LLContextMenuBranch::Params p;
  3413. p.name = menu->getName();
  3414. p.label = menu->getLabel();
  3415. p.branch = menu;
  3416. p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
  3417. p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
  3418. p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
  3419. p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
  3420. item = LLUICtrlFactory::create<LLContextMenuBranch>(p);
  3421. LLMenuGL::sMenuContainer->addChild(item->getBranch());
  3422. return append( item );
  3423. }
  3424. bool LLContextMenu::addChild(LLView* view, S32 tab_group)
  3425. {
  3426. LLContextMenu* context = dynamic_cast<LLContextMenu*>(view);
  3427. if (context)
  3428. return appendContextSubMenu(context);
  3429. LLMenuItemSeparatorGL* separator = dynamic_cast<LLMenuItemSeparatorGL*>(view);
  3430. if (separator)
  3431. return append(separator);
  3432. LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(view);
  3433. if (item)
  3434. return append(item);
  3435. return false;
  3436. }