Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

466 lines
13 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: dlgmgrc.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains client side dialog functionality
  7. *
  8. * History:
  9. * 15-Dec-1993 JohnC Pulled functions from user\server.
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. /***************************************************************************\
  14. * UT_PrevGroupItem
  15. *
  16. * History:
  17. \***************************************************************************/
  18. PWND UT_PrevGroupItem(
  19. PWND pwndDlg,
  20. PWND pwndCurrent)
  21. {
  22. PWND pwnd, pwndPrev;
  23. if (pwndCurrent == NULL || !TestWF(pwndCurrent, WFGROUP))
  24. return _PrevControl(pwndDlg, pwndCurrent, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
  25. pwndPrev = pwndCurrent;
  26. while (TRUE) {
  27. pwnd = _NextControl(pwndDlg, pwndPrev, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
  28. if (TestWF(pwnd, WFGROUP) || pwnd == pwndCurrent)
  29. return pwndPrev;
  30. pwndPrev = pwnd;
  31. }
  32. }
  33. /***************************************************************************\
  34. * UT_NextGroupItem
  35. *
  36. * History:
  37. \***************************************************************************/
  38. PWND UT_NextGroupItem(
  39. PWND pwndDlg,
  40. PWND pwndCurrent)
  41. {
  42. PWND pwnd, pwndNext;
  43. pwnd = _NextControl(pwndDlg, pwndCurrent, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
  44. if (pwndCurrent == NULL || !TestWF(pwnd, WFGROUP))
  45. return pwnd;
  46. pwndNext = pwndCurrent;
  47. while (!TestWF(pwndNext, WFGROUP)) {
  48. pwnd = _PrevControl(pwndDlg, pwndNext, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
  49. if (pwnd == pwndCurrent)
  50. return pwndNext;
  51. pwndNext = pwnd;
  52. }
  53. return pwndNext;
  54. }
  55. /***************************************************************************\
  56. * _PrevControl
  57. *
  58. * History:
  59. \***************************************************************************/
  60. PWND _PrevControl(
  61. PWND pwndRoot,
  62. PWND pwndStart,
  63. UINT uFlags)
  64. {
  65. BOOL fFirstFound;
  66. PWND pwndNext;
  67. PWND pwnd, pwndFirst;
  68. if (!pwndStart)
  69. return(NULL);
  70. UserAssert(pwndRoot != pwndStart);
  71. UserAssert(!TestWF(pwndStart, WEFCONTROLPARENT));
  72. pwnd = _NextControl(pwndRoot, NULL, uFlags);
  73. pwndFirst = pwnd;
  74. fFirstFound = FALSE;
  75. while (pwndNext = _NextControl(pwndRoot, pwnd, uFlags)) {
  76. if (pwndNext == pwndStart)
  77. break;
  78. if (pwndNext == pwndFirst) {
  79. if (fFirstFound) {
  80. RIPMSG0(RIP_WARNING, "_PrevControl: Loop Detected");
  81. break;
  82. } else {
  83. fFirstFound = TRUE;
  84. }
  85. }
  86. pwnd = pwndNext;
  87. }
  88. return pwnd;
  89. }
  90. /***************************************************************************\
  91. *
  92. * GetChildControl()
  93. *
  94. * Gets valid ancestor of given window.
  95. * A valid dialog control is a direct descendant of a "form" control.
  96. *
  97. \***************************************************************************/
  98. PWND _GetChildControl(PWND pwndRoot, PWND pwndChild) {
  99. PWND pwndControl = NULL;
  100. while (pwndChild && TestwndChild(pwndChild) && (pwndChild != pwndRoot)) {
  101. pwndControl = pwndChild;
  102. pwndChild = REBASEPWND(pwndChild, spwndParent);
  103. if (TestWF(pwndChild, WEFCONTROLPARENT))
  104. break;
  105. }
  106. return(pwndControl);
  107. }
  108. /***************************************************************************\
  109. *
  110. * _NextSibblingOrAncestor
  111. *
  112. * Called by _NextControl. It returns the next control to pwndStart. If there
  113. * is a next window (pwndStart->spwndNext), then that is it.
  114. * Otherwise, the next control is up the parent chain. However, if it's already
  115. * at the top of the chain (pwndRoot == pwndStart->spwndParent), then the next
  116. * control is the first child of pwndRoot. But if it's not at the top of the chain,
  117. * then the next control is pwndStart->spwndParent or an ancestor.
  118. *
  119. \***************************************************************************/
  120. PWND _NextSibblingOrAncestor (PWND pwndRoot, PWND pwndStart)
  121. {
  122. PWND pwndParent;
  123. #if DBG
  124. PWND pwndNext;
  125. #endif
  126. // If there is a sibbling, go for it
  127. if (pwndStart->spwndNext != NULL) {
  128. return (REBASEALWAYS(pwndStart, spwndNext));
  129. }
  130. // If it cannot go up the parent chain, then return the first sibbling.
  131. pwndParent = REBASEALWAYS(pwndStart, spwndParent);
  132. if (pwndParent == pwndRoot) {
  133. // Note that if pwndStart doesn't have any sibblings,
  134. // this will return pwndStart again
  135. return (REBASEALWAYS(pwndParent, spwndChild));
  136. }
  137. // Otherwise walk up the parent chain looking for the first window with
  138. // a WS_EX_CONTROLPARENT parent.
  139. #if DBG
  140. pwndNext =
  141. #else
  142. return
  143. #endif
  144. _GetChildControl(pwndRoot, pwndParent);
  145. #if DBG
  146. if ((pwndNext != pwndParent) || !TestWF(pwndParent, WEFCONTROLPARENT)) {
  147. // Code looping through the controls in a dialog might go into an infinite
  148. // loop because of this (i.e., xxxRemoveDefaultButton, _GetNextDlgTabItem,..)
  149. // We've walked up the parent chain but will never walk down the child chain again
  150. // because there is a NON WS_EX_CONTROLPARENT parent window somewhere in the chain.
  151. RIPMSG0 (RIP_ERROR, "_NextSibblingOrAncestor: Non WS_EX_CONTROLPARENT window in parent chain");
  152. }
  153. return pwndNext;
  154. #endif
  155. }
  156. /***************************************************************************\
  157. *
  158. * _NextControl()
  159. *
  160. * It searches for the next NON WS_EX_CONTROLPARENT control following pwndStart.
  161. * If pwndStart is NULL, the search begins with pwndRoot's first child;
  162. * otherwise, it starts with the control next to pwndStart.
  163. * This is a depth-first search that can start anywhere in the window tree.
  164. * uFlags determine what WS_EX_CONTROLPARENT windows should be skipped or recursed into.
  165. * If skipping a window, the search moves to the next control (see _NextSibblingOrAncestor);
  166. * otherwise, the search walks down the child chain (recursive call).
  167. * If the search fails, it returns pwndRoot.
  168. *
  169. \***************************************************************************/
  170. PWND _NextControl(
  171. PWND pwndRoot,
  172. PWND pwndStart,
  173. UINT uFlags)
  174. {
  175. BOOL fSkip, fAncestor;
  176. PWND pwndLast, pwndSibblingLoop;
  177. /* Bug 272874 - joejo
  178. *
  179. * Stop infinite loop by only looping a finite number of times and
  180. * then bailing.
  181. */
  182. int nLoopCount = 0;
  183. UserAssert (pwndRoot != NULL);
  184. if (pwndStart == NULL) {
  185. // Start with pwndRoot's first child
  186. pwndStart = REBASEPWND(pwndRoot, spwndChild);
  187. pwndLast = pwndStart;
  188. fAncestor = FALSE;
  189. } else {
  190. UserAssert ((pwndRoot != pwndStart) && _IsDescendant(pwndRoot, pwndStart));
  191. // Save starting handle and get next one
  192. pwndLast = pwndStart;
  193. pwndSibblingLoop = pwndStart;
  194. fAncestor = TRUE;
  195. goto TryNextOne;
  196. }
  197. // If no more controls, game over
  198. if (pwndStart == NULL) {
  199. return pwndRoot;
  200. }
  201. // Search for a non WS_EX_CONTROLPARENT window; if a window should be skipped,
  202. // try its spwndNext; otherwise, walk down its child chain.
  203. pwndSibblingLoop = pwndStart;
  204. do {
  205. //If not WS_EX_CONTROLPARENT parent, done.
  206. if (!TestWF(pwndStart, WEFCONTROLPARENT)) {
  207. return pwndStart;
  208. }
  209. // Do they want to skip this window?
  210. fSkip = ((uFlags & CWP_SKIPINVISIBLE) && !TestWF(pwndStart, WFVISIBLE))
  211. || ((uFlags & CWP_SKIPDISABLED) && TestWF(pwndStart, WFDISABLED));
  212. // Remember the current window
  213. pwndLast = pwndStart;
  214. // Walk down child chain?
  215. if (!fSkip && !fAncestor) {
  216. pwndStart = _NextControl (pwndStart, NULL, uFlags);
  217. // If it found one, done.
  218. if (pwndStart != pwndLast) {
  219. return pwndStart;
  220. }
  221. }
  222. TryNextOne:
  223. // Try the next one.
  224. pwndStart = _NextSibblingOrAncestor (pwndRoot, pwndStart);
  225. if (pwndStart == NULL) {
  226. break;
  227. }
  228. // If parents are the same, we are still in the same sibbling chain
  229. if (pwndLast->spwndParent == pwndStart->spwndParent) {
  230. // If we had just moved up the parent chain last time around,
  231. // mark this as the beginning of the new sibbling chain.
  232. // Otherwise, check if we've looped through all sibblings already.
  233. if (fAncestor) {
  234. // Beggining of new sibbling chain.
  235. pwndSibblingLoop = pwndStart;
  236. } else if (pwndStart == pwndSibblingLoop) {
  237. // Already visited all sibblings, so done.
  238. break;
  239. }
  240. fAncestor = FALSE;
  241. } else {
  242. // We must have moved up the parent chain, so don't
  243. // walk down the child chain right away (try the next window first)
  244. // Eventhough we are on a new sibbling chain, we don't update
  245. // pwndSibblingLoop yet; this is because we must walk down this
  246. // child chain again to make sure we visit all the descendents
  247. fAncestor = TRUE;
  248. }
  249. /* Bug 272874 - joejo
  250. *
  251. * Stop infinite loop by only looping a finite number of times and
  252. * then bailing.
  253. */
  254. } while (nLoopCount++ < 256 * 4);
  255. // It couldn't find one...
  256. return pwndRoot;
  257. }
  258. /***************************************************************************\
  259. * GetNextDlgTabItem
  260. *
  261. * History:
  262. * 19-Feb-1991 JimA Added access check
  263. \***************************************************************************/
  264. FUNCLOG3(LOG_GENERAL, HWND, WINAPI, GetNextDlgTabItem, HWND, hwndDlg, HWND, hwnd, BOOL, fPrev)
  265. HWND WINAPI GetNextDlgTabItem(
  266. HWND hwndDlg,
  267. HWND hwnd,
  268. BOOL fPrev)
  269. {
  270. PWND pwnd;
  271. PWND pwndDlg;
  272. PWND pwndNext;
  273. pwndDlg = ValidateHwnd(hwndDlg);
  274. if (pwndDlg == NULL)
  275. return NULL;
  276. if (hwnd != (HWND)0) {
  277. pwnd = ValidateHwnd(hwnd);
  278. if (pwnd == NULL)
  279. return NULL;
  280. } else {
  281. pwnd = (PWND)NULL;
  282. }
  283. pwndNext = _GetNextDlgTabItem(pwndDlg, pwnd, fPrev);
  284. return (HW(pwndNext));
  285. }
  286. PWND _GetNextDlgTabItem(
  287. PWND pwndDlg,
  288. PWND pwnd,
  289. BOOL fPrev)
  290. {
  291. PWND pwndSave;
  292. if (pwnd == pwndDlg)
  293. pwnd = NULL;
  294. else
  295. {
  296. pwnd = _GetChildControl(pwndDlg, pwnd);
  297. if (pwnd && !_IsDescendant(pwndDlg, pwnd))
  298. return(NULL);
  299. }
  300. //
  301. // BACKWARD COMPATIBILITY
  302. //
  303. // Note that the result when there are no tabstops of
  304. // IGetNextDlgTabItem(pwndDlg, NULL, FALSE) was the last item, now
  305. // will be the first item. We could put a check for fRecurse here
  306. // and do the old thing if not set.
  307. //
  308. // We are going to bug out if we hit the first child a second time.
  309. pwndSave = pwnd;
  310. pwnd = (fPrev ? _PrevControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED) :
  311. _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED));
  312. if (!pwnd)
  313. goto AllOver;
  314. while ((pwnd != pwndSave) && (pwnd != pwndDlg)) {
  315. UserAssert(pwnd);
  316. if (!pwndSave)
  317. pwndSave = pwnd;
  318. if ((pwnd->style & (WS_TABSTOP | WS_VISIBLE | WS_DISABLED)) == (WS_TABSTOP | WS_VISIBLE))
  319. // Found it.
  320. break;
  321. pwnd = (fPrev ? _PrevControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED) :
  322. _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED));
  323. }
  324. AllOver:
  325. return pwnd;
  326. }
  327. /***************************************************************************\
  328. *
  329. * _GetNextDlgGroupItem()
  330. *
  331. \***************************************************************************/
  332. FUNCLOG3(LOG_GENERAL, HWND, DUMMYCALLINGTYPE, GetNextDlgGroupItem, HWND, hwndDlg, HWND, hwndCtl, BOOL, bPrevious)
  333. HWND GetNextDlgGroupItem(
  334. HWND hwndDlg,
  335. HWND hwndCtl,
  336. BOOL bPrevious)
  337. {
  338. PWND pwndDlg;
  339. PWND pwndCtl;
  340. PWND pwndNext;
  341. pwndDlg = ValidateHwnd(hwndDlg);
  342. if (pwndDlg == NULL)
  343. return 0;
  344. if (hwndCtl != (HWND)0) {
  345. pwndCtl = ValidateHwnd(hwndCtl);
  346. if (pwndCtl == NULL)
  347. return 0;
  348. } else {
  349. pwndCtl = (PWND)NULL;
  350. }
  351. if (pwndCtl == pwndDlg)
  352. pwndCtl = pwndDlg;
  353. pwndNext = _GetNextDlgGroupItem(pwndDlg, pwndCtl, bPrevious);
  354. return (HW(pwndNext));
  355. }
  356. PWND _GetNextDlgGroupItem(
  357. PWND pwndDlg,
  358. PWND pwnd,
  359. BOOL fPrev)
  360. {
  361. PWND pwndCurrent;
  362. BOOL fOnceAround = FALSE;
  363. pwnd = pwndCurrent = _GetChildControl(pwndDlg, pwnd);
  364. do {
  365. pwnd = (fPrev ? UT_PrevGroupItem(pwndDlg, pwnd) :
  366. UT_NextGroupItem(pwndDlg, pwnd));
  367. if (pwnd == pwndCurrent)
  368. fOnceAround = TRUE;
  369. if (!pwndCurrent)
  370. pwndCurrent = pwnd;
  371. }
  372. while (!fOnceAround && ((TestWF(pwnd, WFDISABLED) || !TestWF(pwnd, WFVISIBLE))));
  373. return pwnd;
  374. }