llmodaldialog.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /**
  2. * @file llmodaldialog.cpp
  3. * @brief LLModalDialog base class
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-2009, 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. #include "linden_common.h"
  33. #include "llmodaldialog.h"
  34. #include "llwindow.h"
  35. #include "llvector2.h"
  36. // static
  37. std::list<LLModalDialog*> LLModalDialog::sModalStack;
  38. LLModalDialog::LLModalDialog(const std::string& title, S32 width, S32 height,
  39. bool modal)
  40. : LLFloater("modal dialog", LLRect(0, height, width, 0), title,
  41. RESIZE_NO, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, DRAG_ON_TOP,
  42. // minimizable and closeable only if not modal. Bordered.
  43. !modal, !modal, true),
  44. mModal(modal)
  45. {
  46. setVisible(false);
  47. setBackgroundVisible(true);
  48. setBackgroundOpaque(true);
  49. centerOnScreen(); // default position
  50. }
  51. LLModalDialog::~LLModalDialog()
  52. {
  53. // don't unlock focus unless we have it
  54. if (gFocusMgr.childHasKeyboardFocus(this))
  55. {
  56. gFocusMgr.unlockFocus();
  57. }
  58. }
  59. // virtual
  60. void LLModalDialog::open()
  61. {
  62. LLHostFloater host; // Make sure we do not ever host a modal dialog
  63. LLFloater::open();
  64. }
  65. void LLModalDialog::reshape(S32 width, S32 height, bool called_from_parent)
  66. {
  67. LLFloater::reshape(width, height, called_from_parent);
  68. centerOnScreen();
  69. }
  70. void LLModalDialog::startModal()
  71. {
  72. if (mModal)
  73. {
  74. // If Modal, Hide the active modal dialog
  75. if (!sModalStack.empty())
  76. {
  77. LLModalDialog* front = sModalStack.front();
  78. front->setVisible(false);
  79. }
  80. // This is a modal dialog. It sucks up all mouse and keyboard
  81. // operations.
  82. gFocusMgr.setMouseCapture(this);
  83. gFocusMgr.setTopCtrl(this);
  84. setFocus(true);
  85. sModalStack.push_front(this);
  86. }
  87. setVisible(true);
  88. }
  89. void LLModalDialog::stopModal()
  90. {
  91. gFocusMgr.unlockFocus();
  92. gFocusMgr.releaseFocusIfNeeded(this);
  93. if (mModal)
  94. {
  95. std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(),
  96. sModalStack.end(),
  97. this);
  98. if (iter != sModalStack.end())
  99. {
  100. sModalStack.erase(iter);
  101. }
  102. else
  103. {
  104. llwarns << "Dialog not in list !" << llendl;
  105. }
  106. }
  107. if (!sModalStack.empty())
  108. {
  109. LLModalDialog* front = sModalStack.front();
  110. front->setVisible(true);
  111. }
  112. }
  113. void LLModalDialog::setVisible(bool visible)
  114. {
  115. if (mModal)
  116. {
  117. if (visible)
  118. {
  119. // This is a modal dialog. It sucks up all mouse and keyboard
  120. // operations.
  121. gFocusMgr.setMouseCapture(this);
  122. // The dialog view is a root view
  123. gFocusMgr.setTopCtrl(this);
  124. setFocus(true);
  125. }
  126. else
  127. {
  128. gFocusMgr.releaseFocusIfNeeded(this);
  129. }
  130. }
  131. LLFloater::setVisible(visible);
  132. }
  133. bool LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask)
  134. {
  135. if (mModal)
  136. {
  137. if (!LLFloater::handleMouseDown(x, y, mask))
  138. {
  139. // Click was outside the panel
  140. make_ui_sound("UISndInvalidOp");
  141. }
  142. }
  143. else
  144. {
  145. LLFloater::handleMouseDown(x, y, mask);
  146. }
  147. return true;
  148. }
  149. bool LLModalDialog::handleHover(S32 x, S32 y, MASK mask)
  150. {
  151. if (childrenHandleHover(x, y, mask) == NULL)
  152. {
  153. gWindowp->setCursor(UI_CURSOR_ARROW);
  154. LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
  155. }
  156. return true;
  157. }
  158. bool LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask)
  159. {
  160. childrenHandleMouseUp(x, y, mask);
  161. return true;
  162. }
  163. bool LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks)
  164. {
  165. childrenHandleScrollWheel(x, y, clicks);
  166. return true;
  167. }
  168. bool LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask)
  169. {
  170. if (!LLFloater::handleDoubleClick(x, y, mask))
  171. {
  172. // Click outside the panel
  173. make_ui_sound("UISndInvalidOp");
  174. }
  175. return true;
  176. }
  177. bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask)
  178. {
  179. childrenHandleRightMouseDown(x, y, mask);
  180. return true;
  181. }
  182. bool LLModalDialog::handleKeyHere(KEY key, MASK mask)
  183. {
  184. LLFloater::handleKeyHere(key, mask);
  185. if (mModal)
  186. {
  187. // Suck up all keystokes except CTRL-Q.
  188. return key != 'Q' || mask != MASK_CONTROL;
  189. }
  190. else
  191. {
  192. // Don't process escape key until message box has been on screen a
  193. // minimal amount of time to avoid accidentally destroying the message
  194. // box when user is hitting escape at the time it appears
  195. if (key == KEY_ESCAPE && mask == MASK_NONE &&
  196. mVisibleTime.getElapsedTimeF32() > 1.0f)
  197. {
  198. close();
  199. return true;
  200. }
  201. }
  202. return false;
  203. }
  204. void LLModalDialog::onClose(bool app_quitting)
  205. {
  206. stopModal();
  207. LLFloater::onClose(app_quitting);
  208. }
  209. // virtual
  210. void LLModalDialog::draw()
  211. {
  212. gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
  213. LLUI::sColorDropShadow, LLUI::sDropShadowFloater);
  214. LLFloater::draw();
  215. if (mModal)
  216. {
  217. // If we've lost focus to a non-child, get it back ASAP.
  218. if (gFocusMgr.getTopCtrl() != this)
  219. {
  220. gFocusMgr.setTopCtrl(this);
  221. }
  222. if (!gFocusMgr.childHasKeyboardFocus(this))
  223. {
  224. setFocus(true);
  225. }
  226. if (!gFocusMgr.childHasMouseCapture(this))
  227. {
  228. gFocusMgr.setMouseCapture(this);
  229. }
  230. }
  231. }
  232. void LLModalDialog::centerOnScreen()
  233. {
  234. LLVector2 window_size = LLUI::getWindowSize();
  235. centerWithin(LLRect(0, 0, ll_roundp(window_size.mV[VX]),
  236. ll_roundp(window_size.mV[VY])));
  237. }
  238. // static
  239. void LLModalDialog::onAppFocusLost()
  240. {
  241. if (!sModalStack.empty())
  242. {
  243. LLModalDialog* instance = LLModalDialog::sModalStack.front();
  244. if (gFocusMgr.childHasMouseCapture(instance))
  245. {
  246. gFocusMgr.setMouseCapture(NULL);
  247. }
  248. if (gFocusMgr.childHasKeyboardFocus(instance))
  249. {
  250. gFocusMgr.setKeyboardFocus(NULL);
  251. }
  252. }
  253. }
  254. // static
  255. void LLModalDialog::onAppFocusGained()
  256. {
  257. if (!sModalStack.empty())
  258. {
  259. LLModalDialog* instance = LLModalDialog::sModalStack.front();
  260. // This is a modal dialog. It sucks up all mouse and keyboard
  261. // operations.
  262. gFocusMgr.setMouseCapture(instance);
  263. instance->setFocus(true);
  264. gFocusMgr.setTopCtrl(instance);
  265. instance->centerOnScreen();
  266. }
  267. }
  268. void LLModalDialog::shutdownModals()
  269. {
  270. // This method is only for use during app shutdown. ~LLModalDialog()
  271. // checks sModalStack, and if the dialog instance is still there, it
  272. // crumps with "Attempt to delete dialog while still in sModalStack!" But
  273. // at app shutdown, all bets are off. If the user asks to shut down the
  274. // app, we shouldn't have to care WHAT's open. Put differently, if a modal
  275. // dialog is so crucial that we can't let the user terminate until s/he
  276. // addresses it, we should reject a termination request. The current state
  277. // of affairs is that we accept it, but then produce an llerrs popup that
  278. // simply makes our software look unreliable.
  279. sModalStack.clear();
  280. }