PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/lltoolbar.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1230 lines | 941 code | 183 blank | 106 comment | 161 complexity | 9f4c875848a2f4205c1f30dee542cf3a MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lltoolbar.cpp
  3. * @author Richard Nelson
  4. * @brief User customizable toolbar class
  5. *
  6. * $LicenseInfo:firstyear=2011&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2011, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "linden_common.h"
  28. #include <boost/foreach.hpp>
  29. #include "lltoolbar.h"
  30. #include "llcommandmanager.h"
  31. #include "llmenugl.h"
  32. #include "lltrans.h"
  33. #include "llinventory.h"
  34. #include "lliconctrl.h"
  35. // uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit
  36. // thanks, MSVC!
  37. //static LLDefaultChildRegistry::Register<LLToolBar> r1("toolbar");
  38. namespace LLToolBarEnums
  39. {
  40. LLLayoutStack::ELayoutOrientation getOrientation(SideType sideType)
  41. {
  42. LLLayoutStack::ELayoutOrientation orientation = LLLayoutStack::HORIZONTAL;
  43. if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT))
  44. {
  45. orientation = LLLayoutStack::VERTICAL;
  46. }
  47. return orientation;
  48. }
  49. }
  50. using namespace LLToolBarEnums;
  51. namespace LLInitParam
  52. {
  53. void TypeValues<ButtonType>::declareValues()
  54. {
  55. declare("icons_with_text", BTNTYPE_ICONS_WITH_TEXT);
  56. declare("icons_only", BTNTYPE_ICONS_ONLY);
  57. }
  58. void TypeValues<SideType>::declareValues()
  59. {
  60. declare("bottom", SIDE_BOTTOM);
  61. declare("left", SIDE_LEFT);
  62. declare("right", SIDE_RIGHT);
  63. declare("top", SIDE_TOP);
  64. }
  65. }
  66. LLToolBar::Params::Params()
  67. : button_display_mode("button_display_mode"),
  68. commands("command"),
  69. side("side", SIDE_TOP),
  70. button_icon("button_icon"),
  71. button_icon_and_text("button_icon_and_text"),
  72. read_only("read_only", false),
  73. wrap("wrap", true),
  74. pad_left("pad_left"),
  75. pad_top("pad_top"),
  76. pad_right("pad_right"),
  77. pad_bottom("pad_bottom"),
  78. pad_between("pad_between"),
  79. min_girth("min_girth"),
  80. button_panel("button_panel")
  81. {}
  82. LLToolBar::LLToolBar(const LLToolBar::Params& p)
  83. : LLUICtrl(p),
  84. mReadOnly(p.read_only),
  85. mButtonType(p.button_display_mode),
  86. mSideType(p.side),
  87. mWrap(p.wrap),
  88. mNeedsLayout(false),
  89. mModified(false),
  90. mButtonPanel(NULL),
  91. mCenteringStack(NULL),
  92. mPadLeft(p.pad_left),
  93. mPadRight(p.pad_right),
  94. mPadTop(p.pad_top),
  95. mPadBottom(p.pad_bottom),
  96. mPadBetween(p.pad_between),
  97. mMinGirth(p.min_girth),
  98. mPopupMenuHandle(),
  99. mRightMouseTargetButton(NULL),
  100. mStartDragItemCallback(NULL),
  101. mHandleDragItemCallback(NULL),
  102. mHandleDropCallback(NULL),
  103. mButtonAddSignal(NULL),
  104. mButtonEnterSignal(NULL),
  105. mButtonLeaveSignal(NULL),
  106. mButtonRemoveSignal(NULL),
  107. mDragAndDropTarget(false)
  108. {
  109. mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text;
  110. mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon;
  111. }
  112. LLToolBar::~LLToolBar()
  113. {
  114. delete mPopupMenuHandle.get();
  115. delete mButtonAddSignal;
  116. delete mButtonEnterSignal;
  117. delete mButtonLeaveSignal;
  118. delete mButtonRemoveSignal;
  119. }
  120. void LLToolBar::createContextMenu()
  121. {
  122. if (!mPopupMenuHandle.get())
  123. {
  124. // Setup bindings specific to this instance for the context menu options
  125. LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg;
  126. commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2));
  127. commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this));
  128. LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg;
  129. enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2));
  130. // Create the context menu
  131. LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
  132. if (menu)
  133. {
  134. menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
  135. mPopupMenuHandle = menu->getHandle();
  136. mRemoveButtonHandle = menu->getChild<LLView>("Remove button")->getHandle();
  137. }
  138. else
  139. {
  140. llwarns << "Unable to load toolbars context menu." << llendl;
  141. }
  142. }
  143. if (mRemoveButtonHandle.get())
  144. {
  145. // Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked
  146. mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL);
  147. }
  148. }
  149. void LLToolBar::initFromParams(const LLToolBar::Params& p)
  150. {
  151. // Initialize the base object
  152. LLUICtrl::initFromParams(p);
  153. LLLayoutStack::ELayoutOrientation orientation = getOrientation(p.side);
  154. LLLayoutStack::Params centering_stack_p;
  155. centering_stack_p.name = "centering_stack";
  156. centering_stack_p.rect = getLocalRect();
  157. centering_stack_p.follows.flags = FOLLOWS_ALL;
  158. centering_stack_p.orientation = orientation;
  159. centering_stack_p.mouse_opaque = false;
  160. mCenteringStack = LLUICtrlFactory::create<LLLayoutStack>(centering_stack_p);
  161. addChild(mCenteringStack);
  162. LLLayoutPanel::Params border_panel_p;
  163. border_panel_p.name = "border_panel";
  164. border_panel_p.rect = getLocalRect();
  165. border_panel_p.auto_resize = true;
  166. border_panel_p.user_resize = false;
  167. border_panel_p.mouse_opaque = false;
  168. mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
  169. LLLayoutPanel::Params center_panel_p;
  170. center_panel_p.name = "center_panel";
  171. center_panel_p.rect = getLocalRect();
  172. center_panel_p.auto_resize = false;
  173. center_panel_p.user_resize = false;
  174. center_panel_p.mouse_opaque = false;
  175. LLLayoutPanel* center_panel = LLUICtrlFactory::create<LLLayoutPanel>(center_panel_p);
  176. mCenteringStack->addChild(center_panel);
  177. LLPanel::Params button_panel_p(p.button_panel);
  178. button_panel_p.rect = center_panel->getLocalRect();
  179. button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT;
  180. mButtonPanel = LLUICtrlFactory::create<LLPanel>(button_panel_p);
  181. center_panel->addChild(mButtonPanel);
  182. mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
  183. BOOST_FOREACH(LLCommandId id, p.commands)
  184. {
  185. addCommand(id);
  186. }
  187. mNeedsLayout = true;
  188. }
  189. bool LLToolBar::addCommand(const LLCommandId& commandId, int rank)
  190. {
  191. LLCommand * command = LLCommandManager::instance().getCommand(commandId);
  192. if (!command) return false;
  193. // Create the button and do the things that don't need ordering
  194. LLToolBarButton* button = createButton(commandId);
  195. mButtonPanel->addChild(button);
  196. mButtonMap.insert(std::make_pair(commandId.uuid(), button));
  197. // Insert the command and button in the right place in their respective lists
  198. if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE))
  199. {
  200. // In that case, back load
  201. mButtonCommands.push_back(command->id());
  202. mButtons.push_back(button);
  203. }
  204. else
  205. {
  206. // Insert in place: iterate to the right spot...
  207. std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
  208. command_id_list_t::iterator it_command = mButtonCommands.begin();
  209. while (rank > 0)
  210. {
  211. ++it_button;
  212. ++it_command;
  213. rank--;
  214. }
  215. // ...then insert
  216. mButtonCommands.insert(it_command, command->id());
  217. mButtons.insert(it_button,button);
  218. }
  219. mNeedsLayout = true;
  220. updateLayoutAsNeeded();
  221. if (mButtonAddSignal)
  222. {
  223. (*mButtonAddSignal)(button);
  224. }
  225. return true;
  226. }
  227. // Remove a command from the list
  228. // Returns the rank of the command in the original list so that doing addCommand(id,rank) right after
  229. // a removeCommand(id) would leave the list unchanged.
  230. // Returns RANK_NONE if the command is not found in the list
  231. int LLToolBar::removeCommand(const LLCommandId& commandId)
  232. {
  233. if (!hasCommand(commandId)) return RANK_NONE;
  234. // First erase the map record
  235. command_id_map::iterator it = mButtonMap.find(commandId.uuid());
  236. mButtonMap.erase(it);
  237. // Now iterate on the commands and buttons to identify the relevant records
  238. int rank = 0;
  239. std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
  240. command_id_list_t::iterator it_command = mButtonCommands.begin();
  241. while (*it_command != commandId)
  242. {
  243. ++it_button;
  244. ++it_command;
  245. ++rank;
  246. }
  247. if (mButtonRemoveSignal)
  248. {
  249. (*mButtonRemoveSignal)(*it_button);
  250. }
  251. // Delete the button and erase the command and button records
  252. delete (*it_button);
  253. mButtonCommands.erase(it_command);
  254. mButtons.erase(it_button);
  255. mNeedsLayout = true;
  256. return rank;
  257. }
  258. void LLToolBar::clearCommandsList()
  259. {
  260. // Clears the commands list
  261. mButtonCommands.clear();
  262. // This will clear the buttons
  263. createButtons();
  264. }
  265. bool LLToolBar::hasCommand(const LLCommandId& commandId) const
  266. {
  267. if (commandId != LLCommandId::null)
  268. {
  269. command_id_map::const_iterator it = mButtonMap.find(commandId.uuid());
  270. return (it != mButtonMap.end());
  271. }
  272. return false;
  273. }
  274. bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled)
  275. {
  276. LLButton * command_button = NULL;
  277. if (commandId != LLCommandId::null)
  278. {
  279. command_id_map::iterator it = mButtonMap.find(commandId.uuid());
  280. if (it != mButtonMap.end())
  281. {
  282. command_button = it->second;
  283. command_button->setEnabled(enabled);
  284. }
  285. }
  286. return (command_button != NULL);
  287. }
  288. bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId)
  289. {
  290. //
  291. // Note from Leslie:
  292. //
  293. // This implementation was largely put in place to handle EXP-1348 which is related to
  294. // dragging and dropping the "speak" button. The "speak" button can be in one of two
  295. // modes, i.e., either a toggle action or a push-to-talk action. Because of this it
  296. // responds to mouse down and mouse up in different ways, based on which behavior the
  297. // button is currently set to obey. This was the simplest way of getting the button
  298. // to turn off the microphone for both behaviors without risking duplicate state.
  299. //
  300. LLToolBarButton * command_button = NULL;
  301. if (commandId != LLCommandId::null)
  302. {
  303. LLCommand* command = LLCommandManager::instance().getCommand(commandId);
  304. llassert(command);
  305. // If this command has an explicit function for execution stop
  306. if (command->executeStopFunctionName().length() > 0)
  307. {
  308. command_id_map::iterator it = mButtonMap.find(commandId.uuid());
  309. if (it != mButtonMap.end())
  310. {
  311. command_button = it->second;
  312. llassert(command_button->mIsRunningSignal);
  313. // Check to see if it is running
  314. if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters()))
  315. {
  316. // Trigger an additional button commit, which calls mouse down, mouse up and commit
  317. command_button->onCommit();
  318. }
  319. }
  320. }
  321. }
  322. return (command_button != NULL);
  323. }
  324. bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash)
  325. {
  326. LLButton * command_button = NULL;
  327. if (commandId != LLCommandId::null)
  328. {
  329. command_id_map::iterator it = mButtonMap.find(commandId.uuid());
  330. if (it != mButtonMap.end())
  331. {
  332. command_button = it->second;
  333. command_button->setFlashing(flash ? TRUE : FALSE);
  334. }
  335. }
  336. return (command_button != NULL);
  337. }
  338. BOOL LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask)
  339. {
  340. LLRect button_panel_rect;
  341. mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this);
  342. BOOL handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y);
  343. if (handle_it_here)
  344. {
  345. // Determine which button the mouse was over during the click in case the context menu action
  346. // is intended to affect the button.
  347. mRightMouseTargetButton = NULL;
  348. BOOST_FOREACH(LLToolBarButton* button, mButtons)
  349. {
  350. LLRect button_rect;
  351. button->localRectToOtherView(button->getLocalRect(), &button_rect, this);
  352. if (button_rect.pointInRect(x, y))
  353. {
  354. mRightMouseTargetButton = button;
  355. break;
  356. }
  357. }
  358. createContextMenu();
  359. LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get();
  360. if (menu)
  361. {
  362. menu->show(x, y);
  363. LLMenuGL::showPopup(this, menu, x, y);
  364. }
  365. }
  366. return handle_it_here;
  367. }
  368. BOOL LLToolBar::isSettingChecked(const LLSD& userdata)
  369. {
  370. BOOL retval = FALSE;
  371. const std::string setting_name = userdata.asString();
  372. if (setting_name == "icons_with_text")
  373. {
  374. retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT);
  375. }
  376. else if (setting_name == "icons_only")
  377. {
  378. retval = (mButtonType == BTNTYPE_ICONS_ONLY);
  379. }
  380. return retval;
  381. }
  382. void LLToolBar::onSettingEnable(const LLSD& userdata)
  383. {
  384. llassert(!mReadOnly);
  385. const std::string setting_name = userdata.asString();
  386. if (setting_name == "icons_with_text")
  387. {
  388. setButtonType(BTNTYPE_ICONS_WITH_TEXT);
  389. }
  390. else if (setting_name == "icons_only")
  391. {
  392. setButtonType(BTNTYPE_ICONS_ONLY);
  393. }
  394. }
  395. void LLToolBar::onRemoveSelectedCommand()
  396. {
  397. llassert(!mReadOnly);
  398. if (mRightMouseTargetButton)
  399. {
  400. removeCommand(mRightMouseTargetButton->getCommandId());
  401. mRightMouseTargetButton = NULL;
  402. }
  403. }
  404. void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type)
  405. {
  406. bool regenerate_buttons = (mButtonType != button_type);
  407. mButtonType = button_type;
  408. if (regenerate_buttons)
  409. {
  410. createButtons();
  411. }
  412. }
  413. void LLToolBar::resizeButtonsInRow(std::vector<LLToolBarButton*>& buttons_in_row, S32 max_row_girth)
  414. {
  415. // make buttons in current row all same girth
  416. BOOST_FOREACH(LLToolBarButton* button, buttons_in_row)
  417. {
  418. if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
  419. {
  420. button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth);
  421. }
  422. else // VERTICAL
  423. {
  424. button->reshape(max_row_girth, button->getRect().getHeight());
  425. }
  426. }
  427. }
  428. // Returns the position of the coordinates as a rank in the button list.
  429. // The rank is the position a tool dropped in (x,y) would assume in the button list.
  430. // The returned value is between 0 and mButtons.size(), 0 being the first element to the left
  431. // (or top) and mButtons.size() the last one to the right (or bottom).
  432. // Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't).
  433. int LLToolBar::getRankFromPosition(S32 x, S32 y)
  434. {
  435. if (mButtons.empty())
  436. {
  437. return RANK_NONE;
  438. }
  439. int rank = 0;
  440. // Convert the toolbar coord into button panel coords
  441. LLLayoutStack::ELayoutOrientation orientation = getOrientation(mSideType);
  442. S32 button_panel_x = 0;
  443. S32 button_panel_y = 0;
  444. localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel);
  445. S32 dx = x - button_panel_x;
  446. S32 dy = y - button_panel_y;
  447. // Simply compare the passed coord with the buttons outbound box + padding
  448. std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
  449. std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
  450. LLRect button_rect;
  451. while (it_button != end_button)
  452. {
  453. button_rect = (*it_button)->getRect();
  454. S32 point_x = button_rect.mRight + mPadRight;
  455. S32 point_y = button_rect.mBottom - mPadBottom;
  456. if ((button_panel_x < point_x) && (button_panel_y > point_y))
  457. {
  458. break;
  459. }
  460. rank++;
  461. ++it_button;
  462. }
  463. // Update the passed coordinates to the hit button relevant corner
  464. // (different depending on toolbar orientation)
  465. if (rank < mButtons.size())
  466. {
  467. if (orientation == LLLayoutStack::HORIZONTAL)
  468. {
  469. // Horizontal
  470. S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2;
  471. if (button_panel_x < mid_point)
  472. {
  473. mDragx = button_rect.mLeft - mPadLeft;
  474. mDragy = button_rect.mTop + mPadTop;
  475. }
  476. else
  477. {
  478. rank++;
  479. mDragx = button_rect.mRight + mPadRight - 1;
  480. mDragy = button_rect.mTop + mPadTop;
  481. }
  482. }
  483. else
  484. {
  485. // Vertical
  486. S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2;
  487. if (button_panel_y > mid_point)
  488. {
  489. mDragx = button_rect.mLeft - mPadLeft;
  490. mDragy = button_rect.mTop + mPadTop;
  491. }
  492. else
  493. {
  494. rank++;
  495. mDragx = button_rect.mLeft - mPadLeft;
  496. mDragy = button_rect.mBottom - mPadBottom + 1;
  497. }
  498. }
  499. }
  500. else
  501. {
  502. // We hit passed the end of the list so put the insertion point at the end
  503. if (orientation == LLLayoutStack::HORIZONTAL)
  504. {
  505. mDragx = button_rect.mRight + mPadRight;
  506. mDragy = button_rect.mTop + mPadTop;
  507. }
  508. else
  509. {
  510. mDragx = button_rect.mLeft - mPadLeft;
  511. mDragy = button_rect.mBottom - mPadBottom;
  512. }
  513. }
  514. // Update the "girth" of the caret, i.e. the width or height (depending of orientation)
  515. if (orientation == LLLayoutStack::HORIZONTAL)
  516. {
  517. mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop;
  518. }
  519. else
  520. {
  521. mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight;
  522. }
  523. // The delta account for the coord model change (i.e. convert back to toolbar coord)
  524. mDragx += dx;
  525. mDragy += dy;
  526. return rank;
  527. }
  528. int LLToolBar::getRankFromPosition(const LLCommandId& id)
  529. {
  530. if (!hasCommand(id))
  531. {
  532. return RANK_NONE;
  533. }
  534. int rank = 0;
  535. std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
  536. std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
  537. while (it_button != end_button)
  538. {
  539. if ((*it_button)->mId == id)
  540. {
  541. break;
  542. }
  543. rank++;
  544. ++it_button;
  545. }
  546. return rank;
  547. }
  548. void LLToolBar::updateLayoutAsNeeded()
  549. {
  550. if (!mNeedsLayout) return;
  551. LLLayoutStack::ELayoutOrientation orientation = getOrientation(mSideType);
  552. // our terminology for orientation-agnostic layout is such that
  553. // length refers to a distance in the direction we stack the buttons
  554. // and girth refers to a distance in the direction buttons wrap
  555. S32 max_row_girth = 0;
  556. S32 max_row_length = 0;
  557. S32 max_length;
  558. S32 max_total_girth;
  559. S32 cur_start;
  560. S32 cur_row ;
  561. S32 row_pad_start;
  562. S32 row_pad_end;
  563. S32 girth_pad_end;
  564. S32 row_running_length;
  565. if (orientation == LLLayoutStack::HORIZONTAL)
  566. {
  567. max_length = getRect().getWidth() - mPadLeft - mPadRight;
  568. max_total_girth = getRect().getHeight() - mPadTop - mPadBottom;
  569. row_pad_start = mPadLeft;
  570. row_pad_end = mPadRight;
  571. cur_row = mPadTop;
  572. girth_pad_end = mPadBottom;
  573. }
  574. else // VERTICAL
  575. {
  576. max_length = getRect().getHeight() - mPadTop - mPadBottom;
  577. max_total_girth = getRect().getWidth() - mPadLeft - mPadRight;
  578. row_pad_start = mPadTop;
  579. row_pad_end = mPadBottom;
  580. cur_row = mPadLeft;
  581. girth_pad_end = mPadRight;
  582. }
  583. row_running_length = row_pad_start;
  584. cur_start = row_pad_start;
  585. LLRect panel_rect = mButtonPanel->getLocalRect();
  586. std::vector<LLToolBarButton*> buttons_in_row;
  587. BOOST_FOREACH(LLToolBarButton* button, mButtons)
  588. {
  589. button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight);
  590. button->autoResize();
  591. S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth());
  592. S32 button_length = (orientation == LLLayoutStack::HORIZONTAL)
  593. ? button_clamped_width
  594. : button->getRect().getHeight();
  595. S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL)
  596. ? button->getRect().getHeight()
  597. : button_clamped_width;
  598. // wrap if needed
  599. if (mWrap
  600. && row_running_length + button_length > max_length // out of room...
  601. && cur_start != row_pad_start) // ...and not first button in row
  602. {
  603. if (orientation == LLLayoutStack::VERTICAL)
  604. { // row girth (width in this case) is clamped to allowable button widths
  605. max_row_girth = button->mWidthRange.clamp(max_row_girth);
  606. }
  607. // make buttons in current row all same girth
  608. resizeButtonsInRow(buttons_in_row, max_row_girth);
  609. buttons_in_row.clear();
  610. max_row_length = llmax(max_row_length, row_running_length);
  611. row_running_length = row_pad_start;
  612. cur_start = row_pad_start;
  613. cur_row += max_row_girth + mPadBetween;
  614. max_row_girth = 0;
  615. }
  616. LLRect button_rect;
  617. if (orientation == LLLayoutStack::HORIZONTAL)
  618. {
  619. button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight());
  620. }
  621. else // VERTICAL
  622. {
  623. button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight());
  624. }
  625. button->setShape(button_rect);
  626. buttons_in_row.push_back(button);
  627. row_running_length += button_length + mPadBetween;
  628. cur_start = row_running_length;
  629. max_row_girth = llmax(button_girth, max_row_girth);
  630. }
  631. // final resizing in "girth" direction
  632. S32 total_girth = cur_row // current row position...
  633. + max_row_girth // ...incremented by size of final row...
  634. + girth_pad_end; // ...plus padding reserved on end
  635. total_girth = llmax(total_girth,mMinGirth);
  636. max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end);
  637. resizeButtonsInRow(buttons_in_row, max_row_girth);
  638. // grow and optionally shift toolbar to accommodate buttons
  639. if (orientation == LLLayoutStack::HORIZONTAL)
  640. {
  641. if (mSideType == SIDE_TOP)
  642. { // shift down to maintain top edge
  643. translate(0, getRect().getHeight() - total_girth);
  644. }
  645. reshape(getRect().getWidth(), total_girth);
  646. mButtonPanel->reshape(max_row_length, total_girth);
  647. }
  648. else // VERTICAL
  649. {
  650. if (mSideType == SIDE_RIGHT)
  651. { // shift left to maintain right edge
  652. translate(getRect().getWidth() - total_girth, 0);
  653. }
  654. reshape(total_girth, getRect().getHeight());
  655. mButtonPanel->reshape(total_girth, max_row_length);
  656. }
  657. // make parent fit button panel
  658. mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect());
  659. // re-center toolbar buttons
  660. mCenteringStack->updateLayout();
  661. if (!mButtons.empty())
  662. {
  663. mButtonPanel->setVisible(TRUE);
  664. mButtonPanel->setMouseOpaque(TRUE);
  665. }
  666. // don't clear flag until after we've resized ourselves, to avoid laying out every frame
  667. mNeedsLayout = false;
  668. }
  669. void LLToolBar::draw()
  670. {
  671. if (mButtons.empty())
  672. {
  673. mButtonPanel->setVisible(FALSE);
  674. mButtonPanel->setMouseOpaque(FALSE);
  675. }
  676. else
  677. {
  678. mButtonPanel->setVisible(TRUE);
  679. mButtonPanel->setMouseOpaque(TRUE);
  680. }
  681. // Update enable/disable state and highlight state for editable toolbars
  682. if (!mReadOnly)
  683. {
  684. for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it)
  685. {
  686. LLToolBarButton* btn = *btn_it;
  687. LLCommand* command = LLCommandManager::instance().getCommand(btn->mId);
  688. if (command && btn->mIsEnabledSignal)
  689. {
  690. const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters());
  691. btn->setEnabled(button_command_enabled);
  692. }
  693. if (command && btn->mIsRunningSignal)
  694. {
  695. const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters());
  696. btn->setToggleState(button_command_running);
  697. }
  698. }
  699. }
  700. updateLayoutAsNeeded();
  701. // rect may have shifted during layout
  702. LLUI::popMatrix();
  703. LLUI::pushMatrix();
  704. LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom, 0.f);
  705. // Position the caret
  706. LLIconCtrl* caret = getChild<LLIconCtrl>("caret");
  707. caret->setVisible(FALSE);
  708. if (mDragAndDropTarget && !mButtonCommands.empty())
  709. {
  710. LLRect caret_rect = caret->getRect();
  711. LLRect toolbar_rect = getRect();
  712. if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
  713. {
  714. caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1,
  715. mDragy,
  716. mDragx+caret_rect.getWidth()/2+1,
  717. mDragy-mDragGirth));
  718. }
  719. else
  720. {
  721. caret->setRect(LLRect(mDragx,
  722. mDragy+caret_rect.getHeight()/2,
  723. mDragx+mDragGirth,
  724. mDragy-caret_rect.getHeight()/2));
  725. }
  726. caret->setVisible(TRUE);
  727. }
  728. LLUICtrl::draw();
  729. caret->setVisible(FALSE);
  730. mDragAndDropTarget = false;
  731. }
  732. void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
  733. {
  734. LLUICtrl::reshape(width, height, called_from_parent);
  735. mNeedsLayout = true;
  736. }
  737. void LLToolBar::createButtons()
  738. {
  739. BOOST_FOREACH(LLToolBarButton* button, mButtons)
  740. {
  741. if (mButtonRemoveSignal)
  742. {
  743. (*mButtonRemoveSignal)(button);
  744. }
  745. delete button;
  746. }
  747. mButtons.clear();
  748. mButtonMap.clear();
  749. mRightMouseTargetButton = NULL;
  750. BOOST_FOREACH(LLCommandId& command_id, mButtonCommands)
  751. {
  752. LLToolBarButton* button = createButton(command_id);
  753. mButtons.push_back(button);
  754. mButtonPanel->addChild(button);
  755. mButtonMap.insert(std::make_pair(command_id.uuid(), button));
  756. if (mButtonAddSignal)
  757. {
  758. (*mButtonAddSignal)(button);
  759. }
  760. }
  761. mNeedsLayout = true;
  762. }
  763. void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param )
  764. {
  765. LLCommand* command = LLCommandManager::instance().getCommand(mId);
  766. if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
  767. {
  768. commit(ctrl, param);
  769. }
  770. }
  771. LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
  772. {
  773. LLCommand* commandp = LLCommandManager::instance().getCommand(id);
  774. if (!commandp) return NULL;
  775. LLToolBarButton::Params button_p;
  776. button_p.name = commandp->name();
  777. button_p.label = LLTrans::getString(commandp->labelRef());
  778. button_p.tool_tip = LLTrans::getString(commandp->tooltipRef());
  779. button_p.image_overlay = LLUI::getUIImage(commandp->icon());
  780. button_p.overwriteFrom(mButtonParams[mButtonType]);
  781. LLToolBarButton* button = LLUICtrlFactory::create<LLToolBarButton>(button_p);
  782. if (!mReadOnly)
  783. {
  784. enable_callback_t isEnabledCB;
  785. const std::string& isEnabledFunction = commandp->isEnabledFunctionName();
  786. if (isEnabledFunction.length() > 0)
  787. {
  788. LLUICtrl::EnableCallbackParam isEnabledParam;
  789. isEnabledParam.function_name = isEnabledFunction;
  790. isEnabledParam.parameter = commandp->isEnabledParameters();
  791. isEnabledCB = initEnableCallback(isEnabledParam);
  792. if (NULL == button->mIsEnabledSignal)
  793. {
  794. button->mIsEnabledSignal = new enable_signal_t();
  795. }
  796. button->mIsEnabledSignal->connect(isEnabledCB);
  797. }
  798. LLUICtrl::CommitCallbackParam executeParam;
  799. executeParam.function_name = commandp->executeFunctionName();
  800. executeParam.parameter = commandp->executeParameters();
  801. // If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit
  802. const std::string& executeStopFunction = commandp->executeStopFunctionName();
  803. if (executeStopFunction.length() > 0)
  804. {
  805. LLUICtrl::CommitCallbackParam executeStopParam;
  806. executeStopParam.function_name = executeStopFunction;
  807. executeStopParam.parameter = commandp->executeStopParameters();
  808. LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam);
  809. LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam);
  810. button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2));
  811. button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2));
  812. }
  813. else
  814. {
  815. button->setCommitCallback(executeParam);
  816. }
  817. // Set up "is running" query callback
  818. const std::string& isRunningFunction = commandp->isRunningFunctionName();
  819. if (isRunningFunction.length() > 0)
  820. {
  821. LLUICtrl::EnableCallbackParam isRunningParam;
  822. isRunningParam.function_name = isRunningFunction;
  823. isRunningParam.parameter = commandp->isRunningParameters();
  824. enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam);
  825. if (NULL == button->mIsRunningSignal)
  826. {
  827. button->mIsRunningSignal = new enable_signal_t();
  828. }
  829. button->mIsRunningSignal->connect(isRunningCB);
  830. }
  831. }
  832. // Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar
  833. button->setStartDragCallback(mStartDragItemCallback);
  834. button->setHandleDragCallback(mHandleDragItemCallback);
  835. button->setCommandId(id);
  836. return button;
  837. }
  838. boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb)
  839. {
  840. if (!signal)
  841. {
  842. signal = new LLToolBar::button_signal_t();
  843. }
  844. return signal->connect(cb);
  845. }
  846. boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb)
  847. {
  848. return connectSignal(mButtonAddSignal, cb);
  849. }
  850. boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb)
  851. {
  852. return connectSignal(mButtonEnterSignal, cb);
  853. }
  854. boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb)
  855. {
  856. return connectSignal(mButtonLeaveSignal, cb);
  857. }
  858. boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb)
  859. {
  860. return connectSignal(mButtonRemoveSignal, cb);
  861. }
  862. BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
  863. EDragAndDropType cargo_type,
  864. void* cargo_data,
  865. EAcceptance* accept,
  866. std::string& tooltip_msg)
  867. {
  868. // If we have a drop callback, that means that we can handle the drop
  869. BOOL handled = (mHandleDropCallback ? TRUE : FALSE);
  870. // if drop is set, it's time to call the callback to get the operation done
  871. if (handled && drop)
  872. {
  873. handled = mHandleDropCallback(cargo_data, x, y ,this);
  874. }
  875. // We accept only single tool drop on toolbars
  876. *accept = (handled ? ACCEPT_YES_SINGLE : ACCEPT_NO);
  877. // We'll use that flag to change the visual aspect of the toolbar target on draw()
  878. mDragAndDropTarget = false;
  879. // Convert drag position into insert position and rank
  880. if (!isReadOnly() && handled && !drop)
  881. {
  882. LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
  883. LLAssetType::EType type = inv_item->getType();
  884. if (type == LLAssetType::AT_WIDGET)
  885. {
  886. LLCommandId dragged_command(inv_item->getUUID());
  887. int orig_rank = getRankFromPosition(dragged_command);
  888. mDragRank = getRankFromPosition(x, y);
  889. // Don't DaD if we're dragging a command on itself
  890. mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank-1) == orig_rank)) ? false : true);
  891. //llinfos << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << llendl;
  892. /* Do the following if you want to animate the button itself
  893. LLCommandId dragged_command(inv_item->getUUID());
  894. removeCommand(dragged_command);
  895. addCommand(dragged_command,rank);
  896. */
  897. }
  898. else
  899. {
  900. handled = FALSE;
  901. }
  902. }
  903. return handled;
  904. }
  905. LLToolBarButton::LLToolBarButton(const Params& p)
  906. : LLButton(p),
  907. mMouseDownX(0),
  908. mMouseDownY(0),
  909. mWidthRange(p.button_width),
  910. mDesiredHeight(p.desired_height),
  911. mId(""),
  912. mIsEnabledSignal(NULL),
  913. mIsRunningSignal(NULL),
  914. mIsStartingSignal(NULL),
  915. mIsDragged(false),
  916. mStartDragItemCallback(NULL),
  917. mHandleDragItemCallback(NULL),
  918. mOriginalImageSelected(p.image_selected),
  919. mOriginalImageUnselected(p.image_unselected),
  920. mOriginalImagePressed(p.image_pressed),
  921. mOriginalImagePressedSelected(p.image_pressed_selected),
  922. mOriginalLabelColor(p.label_color),
  923. mOriginalLabelColorSelected(p.label_color_selected),
  924. mOriginalImageOverlayColor(p.image_overlay_color),
  925. mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color)
  926. {
  927. }
  928. LLToolBarButton::~LLToolBarButton()
  929. {
  930. delete mIsEnabledSignal;
  931. delete mIsRunningSignal;
  932. delete mIsStartingSignal;
  933. }
  934. BOOL LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask)
  935. {
  936. mMouseDownX = x;
  937. mMouseDownY = y;
  938. return LLButton::handleMouseDown(x, y, mask);
  939. }
  940. BOOL LLToolBarButton::handleHover(S32 x, S32 y, MASK mask)
  941. {
  942. BOOL handled = FALSE;
  943. S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY);
  944. S32 drag_threshold = LLUI::sSettingGroups["config"]->getS32("DragAndDropDistanceThreshold");
  945. if (mouse_distance_squared > drag_threshold * drag_threshold
  946. && hasMouseCapture() &&
  947. mStartDragItemCallback && mHandleDragItemCallback)
  948. {
  949. if (!mIsDragged)
  950. {
  951. mStartDragItemCallback(x, y, this);
  952. mIsDragged = true;
  953. handled = TRUE;
  954. }
  955. else
  956. {
  957. handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET);
  958. }
  959. }
  960. else
  961. {
  962. handled = LLButton::handleHover(x, y, mask);
  963. }
  964. return handled;
  965. }
  966. void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask)
  967. {
  968. LLUICtrl::onMouseEnter(x, y, mask);
  969. // Always highlight toolbar buttons, even if they are disabled
  970. if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)
  971. {
  972. mNeedsHighlight = TRUE;
  973. }
  974. LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
  975. if (parent_toolbar && parent_toolbar->mButtonEnterSignal)
  976. {
  977. (*(parent_toolbar->mButtonEnterSignal))(this);
  978. }
  979. }
  980. void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask)
  981. {
  982. LLButton::onMouseLeave(x, y, mask);
  983. LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
  984. if (parent_toolbar && parent_toolbar->mButtonLeaveSignal)
  985. {
  986. (*(parent_toolbar->mButtonLeaveSignal))(this);
  987. }
  988. }
  989. void LLToolBarButton::onMouseCaptureLost()
  990. {
  991. mIsDragged = false;
  992. }
  993. void LLToolBarButton::onCommit()
  994. {
  995. LLCommand* command = LLCommandManager::instance().getCommand(mId);
  996. if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
  997. {
  998. LLButton::onCommit();
  999. }
  1000. }
  1001. void LLToolBarButton::reshape(S32 width, S32 height, BOOL called_from_parent)
  1002. {
  1003. LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent);
  1004. }
  1005. void LLToolBarButton::setEnabled(BOOL enabled)
  1006. {
  1007. if (enabled)
  1008. {
  1009. mImageSelected = mOriginalImageSelected;
  1010. mImageUnselected = mOriginalImageUnselected;
  1011. mImagePressed = mOriginalImagePressed;
  1012. mImagePressedSelected = mOriginalImagePressedSelected;
  1013. mUnselectedLabelColor = mOriginalLabelColor;
  1014. mSelectedLabelColor = mOriginalLabelColorSelected;
  1015. mImageOverlayColor = mOriginalImageOverlayColor;
  1016. mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor;
  1017. }
  1018. else
  1019. {
  1020. mImageSelected = mImageDisabledSelected;
  1021. mImageUnselected = mImageDisabled;
  1022. mImagePressed = mImageDisabled;
  1023. mImagePressedSelected = mImageDisabledSelected;
  1024. mUnselectedLabelColor = mDisabledLabelColor;
  1025. mSelectedLabelColor = mDisabledSelectedLabelColor;
  1026. mImageOverlayColor = mImageOverlayDisabledColor;
  1027. mImageOverlaySelectedColor = mImageOverlayDisabledColor;
  1028. }
  1029. }
  1030. const std::string LLToolBarButton::getToolTip() const
  1031. {
  1032. std::string tooltip;
  1033. if (labelIsTruncated() || getCurrentLabel().empty())
  1034. {
  1035. tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip();
  1036. }
  1037. else
  1038. {
  1039. tooltip = LLView::getToolTip();
  1040. }
  1041. LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
  1042. if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0)
  1043. {
  1044. tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")";
  1045. }
  1046. return tooltip;
  1047. }