Source code of Windows XP (NT5)
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.

1988 lines
59 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: userprop.cpp
  3. Description: Provides implementations for quota user property page.
  4. Revision History:
  5. Date Description Programmer
  6. -------- --------------------------------------------------- ----------
  7. 08/15/96 Initial creation. BrianAu
  8. 06/25/98 Replaced AddUserPropSheet with AddUserDialog. BrianAu
  9. Now that we're getting user info from the DS
  10. object picker, the prop sheet idea doesn't work
  11. so well. A std dialog is better.
  12. */
  13. ///////////////////////////////////////////////////////////////////////////////
  14. #include "pch.h" // PCH
  15. #pragma hdrstop
  16. #include <lm.h>
  17. #include "undo.h"
  18. #include "userprop.h"
  19. #include "userbat.h"
  20. #include "uihelp.h"
  21. #include "progress.h"
  22. #include "uiutils.h"
  23. //
  24. // Context help IDs.
  25. //
  26. #pragma data_seg(".text", "CODE")
  27. const static DWORD rgUserPropSheetHelpIDs[] =
  28. {
  29. IDC_ICON_USER, DWORD(-1),
  30. IDC_STATIC2, DWORD(-1),
  31. IDC_TXT_USERNAME, IDH_TXT_USERNAME,
  32. IDC_TXT_SPACEUSED, IDH_TXT_SPACEUSED,
  33. IDC_TXT_SPACEREMAINING, IDH_TXT_SPACEREMAINING,
  34. IDC_LBL_SPACEUSED, DWORD(-1),
  35. IDC_LBL_SPACEREMAINING, DWORD(-1),
  36. IDC_ICON_USERSTATUS, IDH_ICON_USERSTATUS,
  37. IDC_RBN_USER_NOLIMIT, IDH_RBN_USER_NOLIMIT,
  38. IDC_RBN_USER_LIMIT, IDH_RBN_USER_LIMIT,
  39. IDC_TXT_WARN_LEVEL, DWORD(-1),
  40. IDC_EDIT_USER_LIMIT, IDH_EDIT_USER_LIMIT,
  41. IDC_EDIT_USER_THRESHOLD, IDH_EDIT_USER_THRESHOLD,
  42. IDC_CMB_USER_LIMIT, IDH_CMB_USER_LIMIT,
  43. IDC_CMB_USER_THRESHOLD, IDH_CMB_USER_THRESHOLD,
  44. 0,0
  45. };
  46. #pragma data_seg()
  47. //
  48. // Messages for querying property page for icon images.
  49. //
  50. #define DQM_QUERY_STATUS_ICON (WM_USER + 1)
  51. #define DQM_QUERY_USER_ICON (WM_USER + 2)
  52. #define DQM_ENABLE_APPLY_BUTTON (WM_USER + 3)
  53. ///////////////////////////////////////////////////////////////////////////////
  54. /* Function: UserPropSheet::UserPropSheet
  55. Description: Constructor for a user property sheet object.
  56. Initializes the members that hold user quota data.
  57. Arguments: None.
  58. Returns: Nothing.
  59. Exceptions: OutOfMemory.
  60. Revision History:
  61. Date Description Programmer
  62. -------- --------------------------------------------------- ----------
  63. 08/15/96 Initial creation. BrianAu
  64. */
  65. ///////////////////////////////////////////////////////////////////////////////
  66. UserPropSheet::UserPropSheet(
  67. PDISKQUOTA_CONTROL pQuotaControl,
  68. const CVolumeID& idVolume,
  69. HWND hWndParent,
  70. LVSelection& LVSel,
  71. UndoList& UndoList
  72. ) : m_cVolumeMaxBytes(0),
  73. m_pQuotaControl(pQuotaControl),
  74. m_UndoList(UndoList),
  75. m_LVSelection(LVSel),
  76. m_hWndParent(hWndParent),
  77. m_bIsDirty(FALSE),
  78. m_bHomogeneousSelection(TRUE), // Assume selection is homogeneous.
  79. m_pxbQuotaLimit(NULL),
  80. m_pxbQuotaThreshold(NULL),
  81. m_idVolume(idVolume),
  82. m_strPageTitle(g_hInstDll, IDS_TITLE_GENERAL),
  83. m_idCtlNextFocus(-1)
  84. {
  85. DBGASSERT((NULL != m_pQuotaControl));
  86. DBGASSERT((NULL != m_hWndParent));
  87. DBGTRACE((DM_UPROP, DL_HIGH, TEXT("UserPropSheet::UserPropSheet")));
  88. m_llQuotaUsed = 0;
  89. m_llQuotaLimit = 0;
  90. m_llQuotaThreshold = 0;
  91. DBGASSERT((0 == iICON_USER_SINGLE));
  92. DBGASSERT((1 == iICON_USER_MULTIPLE));
  93. m_hIconUser[0] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_SINGLE_USER));
  94. m_hIconUser[1] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_MULTI_USER));
  95. DBGASSERT((0 == iICON_STATUS_OK));
  96. DBGASSERT((1 == iICON_STATUS_OVER_THRESHOLD));
  97. DBGASSERT((2 == iICON_STATUS_OVER_LIMIT));
  98. m_hIconStatus[0] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_OKBUBBLE));
  99. m_hIconStatus[1] = LoadIcon(NULL, IDI_WARNING);
  100. m_hIconStatus[2] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_WARNERR));
  101. }
  102. UserPropSheet::~UserPropSheet(
  103. VOID
  104. )
  105. {
  106. DBGTRACE((DM_UPROP, DL_HIGH, TEXT("UserPropSheet::~UserPropSheet")));
  107. INT i = 0;
  108. if (NULL != m_pQuotaControl)
  109. m_pQuotaControl->Release();
  110. if (NULL != m_pxbQuotaLimit)
  111. delete m_pxbQuotaLimit;
  112. if (NULL != m_pxbQuotaThreshold)
  113. delete m_pxbQuotaThreshold;
  114. }
  115. ///////////////////////////////////////////////////////////////////////////////
  116. /* Function: UserPropSheet::Run
  117. Description: Creates and runs the property sheet dialog.
  118. This is the only method a client needs to call once the object
  119. is created.
  120. Arguments: None.
  121. Returns:
  122. NO_ERROR
  123. E_FAIL - Couldn't create property sheet.
  124. Revision History:
  125. Date Description Programmer
  126. -------- --------------------------------------------------- ----------
  127. 08/15/96 Initial creation. BrianAu
  128. */
  129. ///////////////////////////////////////////////////////////////////////////////
  130. HRESULT
  131. UserPropSheet::Run(
  132. VOID
  133. )
  134. {
  135. HRESULT hResult = NO_ERROR; // Assume success.
  136. PROPSHEETHEADER psh;
  137. PROPSHEETPAGE psp;
  138. ZeroMemory(&psh, sizeof(psh));
  139. ZeroMemory(&psp, sizeof(psp));
  140. //
  141. // Define page.
  142. //
  143. psp.dwSize = sizeof(PROPSHEETPAGE);
  144. psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
  145. psp.hInstance = g_hInstDll;
  146. psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_USERQUOTA);
  147. psp.pszTitle = (LPCTSTR)m_strPageTitle;
  148. psp.pfnDlgProc = DlgProc;
  149. psp.lParam = (LPARAM)this;
  150. psp.pcRefParent = (UINT *)& g_cRefThisDll;
  151. //
  152. // Define sheet.
  153. //
  154. psh.dwSize = sizeof(PROPSHEETHEADER);
  155. psh.dwFlags = PSH_PROPSHEETPAGE;
  156. psh.hwndParent = m_hWndParent;
  157. psh.hInstance = g_hInstDll;
  158. psh.pszIcon = NULL;
  159. psh.pszCaption = NULL;
  160. psh.nPages = 1;
  161. psh.nStartPage = 0;
  162. psh.ppsp = (LPCPROPSHEETPAGE)&psp;
  163. if (0 <= PropertySheet(&psh))
  164. hResult = E_FAIL;
  165. return hResult;
  166. }
  167. ///////////////////////////////////////////////////////////////////////////////
  168. /* Function: UserPropSheet::DlgProc
  169. Description: Static method called by windows to process messages for the
  170. property page dialog. Since it's static, we have to save the "this"
  171. pointer in the window's USERDATA.
  172. Arguments: Standard WndProc-type arguments.
  173. Returns: Standard WndProc-type return values.
  174. Revision History:
  175. Date Description Programmer
  176. -------- --------------------------------------------------- ----------
  177. 08/15/96 Initial creation. BrianAu
  178. */
  179. ///////////////////////////////////////////////////////////////////////////////
  180. INT_PTR APIENTRY
  181. UserPropSheet::DlgProc(
  182. HWND hDlg,
  183. UINT message,
  184. WPARAM wParam,
  185. LPARAM lParam
  186. )
  187. {
  188. INT_PTR bResult = FALSE;
  189. //
  190. // Retrieve the "this" pointer from the dialog's userdata.
  191. // It was placed there in OnInitDialog().
  192. //
  193. UserPropSheet *pThis = (UserPropSheet *)GetWindowLongPtr(hDlg, DWLP_USER);
  194. try
  195. {
  196. switch(message)
  197. {
  198. case WM_INITDIALOG:
  199. DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_INITDIALOG")));
  200. bResult = OnInitDialog(hDlg, wParam, lParam);
  201. break;
  202. case WM_NOTIFY:
  203. DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY")));
  204. bResult = pThis->OnNotify(hDlg, wParam, lParam);
  205. break;
  206. case WM_COMMAND:
  207. DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_COMMAND")));
  208. bResult = pThis->OnCommand(hDlg, wParam, lParam);
  209. break;
  210. case WM_HELP:
  211. DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_HELP")));
  212. bResult = pThis->OnHelp(hDlg, wParam, lParam);
  213. break;
  214. case WM_CONTEXTMENU:
  215. bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  216. break;
  217. case WM_DESTROY:
  218. DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_DESTROY")));
  219. break;
  220. case WM_CLOSE:
  221. case WM_ENDSESSION:
  222. DestroyWindow(hDlg);
  223. break;
  224. case DQM_ENABLE_APPLY_BUTTON:
  225. pThis->m_bIsDirty = TRUE;
  226. bResult = PropSheet_Changed(GetParent(hDlg), hDlg);
  227. break;
  228. //
  229. // These two icon query messages are for automated testing
  230. // of the UI.
  231. //
  232. case DQM_QUERY_USER_ICON:
  233. bResult = pThis->QueryUserIcon(hDlg);
  234. break;
  235. case DQM_QUERY_STATUS_ICON:
  236. bResult = pThis->QueryUserStatusIcon(hDlg);
  237. break;
  238. default:
  239. break;
  240. }
  241. }
  242. catch(CAllocException& e)
  243. {
  244. DiskQuotaMsgBox(GetDesktopWindow(),
  245. IDS_OUTOFMEMORY,
  246. IDS_TITLE_DISK_QUOTA,
  247. MB_ICONERROR | MB_OK);
  248. }
  249. return bResult;
  250. }
  251. ///////////////////////////////////////////////////////////////////////////////
  252. /* Function: UserPropSheet::OnInitDialog
  253. Description: Handler for WM_INITDIALOG. Retrieves the "this" pointer from
  254. the PROPSHEETPAGE structure (pointed to by lParam) and saves it in
  255. the window's USERDATA.
  256. Arguments:
  257. hDlg - Dialog window handle.
  258. wParam - Handle of control to receive focus if we return FALSE.
  259. lParam - Pointer to PROPSHEETPAGE structure for the property page.
  260. Returns:
  261. TRUE = Tells windows to assign focus to the control in wParam.
  262. Exceptions: OutOfMemory.
  263. Revision History:
  264. Date Description Programmer
  265. -------- --------------------------------------------------- ----------
  266. 08/15/96 Initial creation. BrianAu
  267. */
  268. ///////////////////////////////////////////////////////////////////////////////
  269. INT_PTR
  270. UserPropSheet::OnInitDialog(
  271. HWND hDlg,
  272. WPARAM wParam,
  273. LPARAM lParam
  274. )
  275. {
  276. HRESULT hResult = NO_ERROR;
  277. PROPSHEETPAGE *pPage = (PROPSHEETPAGE *)lParam;
  278. UserPropSheet *pThis = (UserPropSheet *)pPage->lParam;
  279. DWORD dwSectorsPerCluster = 0;
  280. DWORD dwBytesPerSector = 0;
  281. DWORD dwFreeClusters = 0;
  282. DWORD dwTotalClusters = 0;
  283. DBGASSERT((NULL != pThis));
  284. //
  285. // Save "this" in the window's userdata.
  286. //
  287. SetWindowLongPtr(hDlg, DWLP_USER, (INT_PTR)pThis);
  288. //
  289. // Read quota info from NTFS.
  290. // For single selection, we cache the selected user's info.
  291. // For multi selection, we cache the defaults for the volume.
  292. // If adding a new user (count == 0), we also use the defaults for the
  293. // volume.
  294. //
  295. pThis->RefreshCachedQuotaInfo();
  296. //
  297. // Calculate the volume's size.
  298. // We'll use this to limit user threshold and quota limit entries.
  299. //
  300. if (GetDiskFreeSpace(pThis->m_idVolume.ForParsing(),
  301. &dwSectorsPerCluster,
  302. &dwBytesPerSector,
  303. &dwFreeClusters,
  304. &dwTotalClusters))
  305. {
  306. pThis->m_cVolumeMaxBytes = (LONGLONG)dwSectorsPerCluster *
  307. (LONGLONG)dwBytesPerSector *
  308. (LONGLONG)dwTotalClusters;
  309. }
  310. pThis->m_pxbQuotaLimit = new XBytes(hDlg,
  311. IDC_EDIT_USER_LIMIT,
  312. IDC_CMB_USER_LIMIT,
  313. pThis->m_llQuotaLimit);
  314. pThis->m_pxbQuotaThreshold = new XBytes(hDlg,
  315. IDC_EDIT_USER_THRESHOLD,
  316. IDC_CMB_USER_THRESHOLD,
  317. pThis->m_llQuotaThreshold);
  318. pThis->InitializeControls(hDlg);
  319. return TRUE; // Set focus to default control.
  320. }
  321. ///////////////////////////////////////////////////////////////////////////////
  322. /* Function: UserPropSheet::RefreshCachedQuotaInfo
  323. Description: Reads the quota limit, threshold and used values from the
  324. property sheet's user object. If multiple users are selected,
  325. only the first one is read.
  326. Arguments: None.
  327. Returns: Result of read operation.
  328. Revision History:
  329. Date Description Programmer
  330. -------- --------------------------------------------------- ----------
  331. 08/15/96 Initial creation. BrianAu
  332. */
  333. ///////////////////////////////////////////////////////////////////////////////
  334. HRESULT
  335. UserPropSheet::RefreshCachedQuotaInfo(
  336. VOID
  337. )
  338. {
  339. HRESULT hResult = NO_ERROR;
  340. PDISKQUOTA_USER pUser = NULL;
  341. INT cSelectedUsers = m_LVSelection.Count();
  342. m_LVSelection.Retrieve(0, &pUser);
  343. //
  344. // Read quota threshold. Multi-user selections use the volume's default.
  345. //
  346. if (1 == cSelectedUsers)
  347. {
  348. hResult = pUser->GetQuotaThreshold(&m_llQuotaThreshold);
  349. }
  350. else
  351. {
  352. hResult = m_pQuotaControl->GetDefaultQuotaThreshold(&m_llQuotaThreshold);
  353. }
  354. if (FAILED(hResult))
  355. goto refresh_quota_info_failed;
  356. //
  357. // Read quota limit. Multi-user selections use the volume's default.
  358. //
  359. if (1 == cSelectedUsers)
  360. {
  361. hResult = pUser->GetQuotaLimit(&m_llQuotaLimit);
  362. }
  363. else
  364. {
  365. hResult = m_pQuotaControl->GetDefaultQuotaLimit(&m_llQuotaLimit);
  366. }
  367. if (FAILED(hResult))
  368. goto refresh_quota_info_failed;
  369. //
  370. // Read quota used.
  371. //
  372. if (1 == cSelectedUsers)
  373. {
  374. hResult = pUser->GetQuotaUsed(&m_llQuotaUsed);
  375. }
  376. else
  377. {
  378. m_llQuotaUsed = 0;
  379. }
  380. refresh_quota_info_failed:
  381. return hResult;
  382. }
  383. ///////////////////////////////////////////////////////////////////////////////
  384. /* Function: UserPropSheet::OnCommand
  385. Description: Handler for WM_COMMAND.
  386. Arguments:
  387. hDlg - Dialog window handle.
  388. wParam - ID of selected control and notification code.
  389. lParam - HWND of selected control.
  390. Returns:
  391. TRUE = Message wasn't handled.
  392. FALSE = Message was handled.
  393. Revision History:
  394. Date Description Programmer
  395. -------- --------------------------------------------------- ----------
  396. 08/15/96 Initial creation. BrianAu
  397. */
  398. ///////////////////////////////////////////////////////////////////////////////
  399. INT_PTR
  400. UserPropSheet::OnCommand(
  401. HWND hDlg,
  402. WPARAM wParam,
  403. LPARAM lParam
  404. )
  405. {
  406. DWORD dwCtlId = LOWORD(wParam);
  407. HWND hWndCtl = (HWND)lParam;
  408. DWORD dwNotifyCode = HIWORD(wParam);
  409. INT_PTR bResult = FALSE;
  410. switch(dwCtlId)
  411. {
  412. case IDC_TXT_USERNAME:
  413. if (EN_SETFOCUS == dwNotifyCode && IDC_EDIT_USER_THRESHOLD == m_idCtlNextFocus)
  414. {
  415. //
  416. // Focus is being set as a result of an invalid entry
  417. // in the warning level field. Force input focus to the
  418. // field and select the entire contents. User can then just
  419. // enter a new value.
  420. //
  421. SetFocus(GetDlgItem(hDlg, IDC_EDIT_USER_THRESHOLD));
  422. SendDlgItemMessage(hDlg, IDC_EDIT_USER_THRESHOLD, EM_SETSEL, 0, -1);
  423. m_idCtlNextFocus = -1;
  424. }
  425. break;
  426. case IDC_RBN_USER_NOLIMIT:
  427. if (m_pxbQuotaThreshold->IsEnabled())
  428. {
  429. //
  430. // This is simple. Just set both the limit and threshold controls
  431. // to "no limit".
  432. //
  433. m_pxbQuotaThreshold->SetBytes(NOLIMIT);
  434. m_pxbQuotaLimit->SetBytes(NOLIMIT);
  435. m_bIsDirty = TRUE;
  436. }
  437. break;
  438. case IDC_RBN_USER_LIMIT:
  439. {
  440. LONGLONG llValue;
  441. //
  442. // This handler needs some logic. We have to handle several
  443. // scenarios/rules with this one.
  444. // 1. Single vs. Multiple selection.
  445. // 2. Single selection for Administrator account.
  446. // 3. Multi selection homogeneous/heterogenous with respect to
  447. // quota limit and threshold values.
  448. // 4. Can't display "No Limit" in edit controls when they're active.
  449. // 5. Use volume defaults for new user and hetergenous multi-select.
  450. //
  451. if (!m_pxbQuotaThreshold->IsEnabled())
  452. {
  453. enum use_types { USE_CACHED, USE_VOLDEF, USE_NOLIMIT };
  454. INT iUseAsValue = USE_CACHED;
  455. INT cSelected = m_LVSelection.Count();
  456. ///////////////////////////////////////////////////////////////
  457. // First set the quota limit controls.
  458. ///////////////////////////////////////////////////////////////
  459. if (0 == cSelected) // Adding new user...
  460. {
  461. iUseAsValue = USE_VOLDEF;
  462. }
  463. else if (1 == cSelected) // One user selected...
  464. {
  465. PDISKQUOTA_USER pUser = NULL;
  466. m_LVSelection.Retrieve(0, &pUser);
  467. if (UserIsAdministrator(pUser))
  468. {
  469. //
  470. // If user is administrator, the limit is always "No Limit".
  471. // This will disable the "Limit" controls and prevent
  472. // user from setting a limit on this account.
  473. //
  474. iUseAsValue = USE_NOLIMIT;
  475. }
  476. else if (NOLIMIT == m_llQuotaLimit)
  477. {
  478. //
  479. // Account isn't Administrator AND the limit is NOLIMIT.
  480. // Use the volume's default "new user" limit value.
  481. //
  482. iUseAsValue = USE_VOLDEF;
  483. }
  484. }
  485. else if (!m_bHomogeneousSelection || NOLIMIT == m_llQuotaLimit) // Multiple user.
  486. {
  487. //
  488. // Multiple non-homogeneous users get the volume's default limit.
  489. // Multiple homogeneous users get their current cached setting unless
  490. // the cached setting is NOLIMIT; in which case, they get the
  491. // volume's defaults.
  492. //
  493. iUseAsValue = USE_VOLDEF;
  494. }
  495. //
  496. // Set the proper quota limit value in the edit/combo box control.
  497. //
  498. llValue = 0;
  499. switch(iUseAsValue)
  500. {
  501. case USE_VOLDEF:
  502. m_pQuotaControl->GetDefaultQuotaLimit(&llValue);
  503. //
  504. // If default is NOLIMIT, display 0 MB. We can't display an
  505. // "editable" No Limit in the edit control. Only numbers.
  506. //
  507. if (NOLIMIT == llValue)
  508. llValue = 0;
  509. break;
  510. case USE_NOLIMIT:
  511. llValue = NOLIMIT;
  512. break;
  513. case USE_CACHED:
  514. llValue = m_llQuotaLimit;
  515. break;
  516. }
  517. m_pxbQuotaLimit->SetBytes(llValue);
  518. ///////////////////////////////////////////////////////////////
  519. // Now the threshold controls...
  520. ///////////////////////////////////////////////////////////////
  521. llValue = 0;
  522. iUseAsValue = USE_CACHED;
  523. if (0 == cSelected)
  524. {
  525. iUseAsValue = USE_VOLDEF;
  526. }
  527. else if (1 == cSelected)
  528. {
  529. if (NOLIMIT == m_llQuotaThreshold)
  530. {
  531. iUseAsValue = USE_VOLDEF;
  532. }
  533. }
  534. else if (!m_bHomogeneousSelection || NOLIMIT == m_llQuotaThreshold)
  535. {
  536. iUseAsValue = USE_VOLDEF;
  537. }
  538. //
  539. // Set the proper quota threshold value in the edit/combo box control.
  540. //
  541. switch(iUseAsValue)
  542. {
  543. case USE_VOLDEF:
  544. m_pQuotaControl->GetDefaultQuotaThreshold(&llValue);
  545. //
  546. // If default is NOLIMIT, display 0 MB. We can't display an
  547. // "editable" No Limit in the edit control. Only numbers.
  548. //
  549. if (NOLIMIT == llValue)
  550. llValue = 0;
  551. break;
  552. case USE_NOLIMIT:
  553. llValue = NOLIMIT;
  554. break;
  555. case USE_CACHED:
  556. llValue = m_llQuotaThreshold;
  557. break;
  558. }
  559. m_pxbQuotaThreshold->SetBytes(llValue);
  560. m_bIsDirty = TRUE;
  561. }
  562. break;
  563. }
  564. case IDC_EDIT_USER_LIMIT:
  565. case IDC_EDIT_USER_THRESHOLD:
  566. switch(dwNotifyCode)
  567. {
  568. case EN_UPDATE:
  569. DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, EN_CHANGE")));
  570. bResult = OnEditNotifyUpdate(hDlg, wParam, lParam);
  571. m_bIsDirty = TRUE;
  572. break;
  573. case EN_KILLFOCUS:
  574. DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, EN_KILLFOCUS")));
  575. bResult = OnEditNotifyKillFocus(hDlg, wParam, lParam);
  576. break;
  577. default:
  578. break;
  579. }
  580. break;
  581. case IDC_CMB_USER_LIMIT:
  582. case IDC_CMB_USER_THRESHOLD:
  583. switch(dwNotifyCode)
  584. {
  585. case CBN_SELCHANGE:
  586. DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, CBN_CHANGE")));
  587. bResult = OnComboNotifySelChange(hDlg, wParam, lParam);
  588. m_bIsDirty = TRUE;
  589. break;
  590. default:
  591. break;
  592. }
  593. break;
  594. default:
  595. bResult = TRUE; // Didn't handle message.
  596. break;
  597. }
  598. if (m_bIsDirty)
  599. PropSheet_Changed(GetParent(hDlg), hDlg);
  600. return bResult;
  601. }
  602. ///////////////////////////////////////////////////////////////////////////////
  603. /* Function: UserPropSheet::OnNotify
  604. Description: Handler for WM_NOTIFY.
  605. Arguments:
  606. hDlg - Dialog window handle.
  607. wParam - ID of selected control and notification code.
  608. lParam - HWND of selected control.
  609. Returns:
  610. TRUE = Message wasn't handled.
  611. FALSE = Message was handled.
  612. Revision History:
  613. Date Description Programmer
  614. -------- --------------------------------------------------- ----------
  615. 08/15/96 Initial creation. BrianAu
  616. */
  617. ///////////////////////////////////////////////////////////////////////////////
  618. INT_PTR
  619. UserPropSheet::OnNotify(
  620. HWND hDlg,
  621. WPARAM wParam,
  622. LPARAM lParam
  623. )
  624. {
  625. INT_PTR bResult = FALSE;
  626. switch(((NMHDR *)lParam)->code)
  627. {
  628. case PSN_SETACTIVE:
  629. DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_SETACTIVE")));
  630. bResult = OnSheetNotifySetActive(hDlg, wParam, lParam);
  631. break;
  632. case PSN_APPLY:
  633. DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_APPLY")));
  634. bResult = OnSheetNotifyApply(hDlg, wParam, lParam);
  635. break;
  636. case PSN_KILLACTIVE:
  637. DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_KILLACTIVE")));
  638. bResult = OnSheetNotifyKillActive(hDlg, wParam, lParam);
  639. break;
  640. case PSN_RESET:
  641. DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_RESET")));
  642. //
  643. // Fall through.
  644. //
  645. default:
  646. break;
  647. }
  648. return bResult;
  649. }
  650. ///////////////////////////////////////////////////////////////////////////////
  651. /* Function: UserPropSheet::OnSheetNotifySetActive
  652. Description: Handler for WM_NOTIFY - PSN_SETACTIVE.
  653. Arguments:
  654. hDlg - Dialog window handle.
  655. wParam - ID of control.
  656. lParam - Address of NMHDR structure.
  657. Returns:
  658. FALSE = Accept activation.
  659. Revision History:
  660. Date Description Programmer
  661. -------- --------------------------------------------------- ----------
  662. 08/15/96 Initial creation. BrianAu
  663. */
  664. ///////////////////////////////////////////////////////////////////////////////
  665. INT_PTR
  666. UserPropSheet::OnSheetNotifySetActive(
  667. HWND hDlg,
  668. WPARAM wParam,
  669. LPARAM lParam
  670. )
  671. {
  672. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0);
  673. return TRUE;
  674. }
  675. ///////////////////////////////////////////////////////////////////////////////
  676. /* Function: UserPropSheet::OnSheetNotifyApply
  677. Description: Handler for WM_NOTIFY - PSN_APPLY.
  678. Arguments:
  679. hDlg - Dialog window handle.
  680. wParam - ID of control.
  681. lParam - Address of NMHDR structure.
  682. Returns:
  683. TRUE = PSN return value set using SetWindowLong.
  684. Revision History:
  685. Date Description Programmer
  686. -------- --------------------------------------------------- ----------
  687. 08/15/96 Initial creation. BrianAu
  688. */
  689. ///////////////////////////////////////////////////////////////////////////////
  690. INT_PTR
  691. UserPropSheet::OnSheetNotifyApply(
  692. HWND hDlg,
  693. WPARAM wParam,
  694. LPARAM lParam
  695. )
  696. {
  697. HRESULT hResult = NO_ERROR;
  698. LONG dwPSNReturn = PSNRET_NOERROR;
  699. //
  700. // Only apply settings if the "Apply" button is enabled indicating
  701. // that something has been changed. No need to apply unchanged
  702. // settings when the OK button is pressed.
  703. //
  704. if (m_bIsDirty)
  705. {
  706. if (PSNRET_NOERROR == dwPSNReturn)
  707. {
  708. //
  709. // We need to do this because if you activate the apply button
  710. // with Alt-A we receive PSN_APPLY before EN_KILLFOCUS.
  711. //
  712. m_pxbQuotaThreshold->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_USER_THRESHOLD));
  713. m_pxbQuotaLimit->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_USER_LIMIT));
  714. //
  715. // Ensure warning threshold is not above limit.
  716. //
  717. INT64 iThreshold = m_pxbQuotaThreshold->GetBytes();
  718. INT64 iLimit = m_pxbQuotaLimit->GetBytes();
  719. if (NOLIMIT != iLimit && iThreshold > iLimit)
  720. {
  721. TCHAR szLimit[40], szThreshold[40];
  722. XBytes::FormatByteCountForDisplay(iLimit, szLimit, ARRAYSIZE(szLimit));
  723. XBytes::FormatByteCountForDisplay(iThreshold, szThreshold, ARRAYSIZE(szThreshold));
  724. CString s(g_hInstDll, IDS_FMT_ERR_WARNOVERLIMIT, szThreshold, szLimit, szLimit);
  725. switch(DiskQuotaMsgBox(hDlg, s, IDS_TITLE_DISK_QUOTA, MB_ICONWARNING | MB_YESNO))
  726. {
  727. case IDYES:
  728. m_pxbQuotaThreshold->SetBytes(iLimit);
  729. break;
  730. case IDNO:
  731. //
  732. // This m_idCtlNextFocus hack stuff is here because I can't get
  733. // the @#$%! prop sheet to return focus to the threshold control.
  734. // The only way I've been able to get this to happen is to
  735. // cache this ID value then on the EN_SETFOCUS generated when
  736. // the page is activated, set focus to the control.
  737. // Gross but it works without too much hassle. [brianau]
  738. //
  739. m_idCtlNextFocus = IDC_EDIT_USER_THRESHOLD;
  740. dwPSNReturn = PSNRET_INVALID;
  741. break;
  742. }
  743. }
  744. }
  745. if (PSNRET_NOERROR == dwPSNReturn)
  746. {
  747. hResult = ApplySettings(hDlg);
  748. if (FAILED(hResult))
  749. {
  750. INT idMsg = IDS_UNKNOWN_ERROR;
  751. UINT uFlags = MB_OK;
  752. switch(hResult)
  753. {
  754. case E_FAIL:
  755. idMsg = IDS_WRITE_ERROR;
  756. uFlags |= MB_ICONERROR;
  757. break;
  758. default:
  759. switch(HRESULT_CODE(hResult))
  760. {
  761. // case ERROR_USER_EXISTS:
  762. // idMsg = IDS_NOADD_EXISTING_USER;
  763. // uFlags |= MB_ICONWARNING;
  764. // break;
  765. //
  766. // FEATURE: Still valid? [brianau - 5/27/98]
  767. //
  768. case ERROR_NO_SUCH_USER:
  769. idMsg = IDS_NOADD_UNKNOWN_USER;
  770. uFlags |= MB_ICONWARNING;
  771. break;
  772. case ERROR_ACCESS_DENIED:
  773. idMsg = IDS_NO_WRITE_ACCESS;
  774. uFlags |= MB_ICONWARNING;
  775. break;
  776. default:
  777. uFlags |= MB_ICONERROR;
  778. break;
  779. }
  780. break;
  781. }
  782. DiskQuotaMsgBox(GetDesktopWindow(),
  783. idMsg,
  784. IDS_TITLE_DISK_QUOTA,
  785. uFlags);
  786. dwPSNReturn = PSNRET_INVALID;
  787. }
  788. else
  789. {
  790. m_bIsDirty = FALSE;
  791. }
  792. }
  793. }
  794. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, dwPSNReturn);
  795. return TRUE;
  796. }
  797. ///////////////////////////////////////////////////////////////////////////////
  798. /* Function: UserPropSheet::OnSheetNotifyKillActive
  799. Description: Handler for WM_NOTIFY - PSN_KILLACTIVE.
  800. Arguments:
  801. hDlg - Dialog window handle.
  802. wParam - ID of control.
  803. lParam - Address of NMHDR structure.
  804. Returns:
  805. TRUE = Invalid data entered. Don't kill page.
  806. FALSE = All data is valid. Ok to kill page.
  807. Revision History:
  808. Date Description Programmer
  809. -------- --------------------------------------------------- ----------
  810. 08/15/96 Initial creation. BrianAu
  811. */
  812. ///////////////////////////////////////////////////////////////////////////////
  813. INT_PTR
  814. UserPropSheet::OnSheetNotifyKillActive(
  815. HWND hDlg,
  816. WPARAM wParam,
  817. LPARAM lParam
  818. )
  819. {
  820. BOOL bAllDataIsValid = TRUE;
  821. //
  822. // No sheet-level validation performed at this time.
  823. //
  824. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, !bAllDataIsValid);
  825. return TRUE;
  826. }
  827. ///////////////////////////////////////////////////////////////////////////////
  828. /* Function: UserPropSheet::OnHelp
  829. Description: Handler for WM_HELP. Displays context sensitive help.
  830. Arguments:
  831. lParam - Pointer to a HELPINFO structure.
  832. Returns: TRUE;
  833. Revision History:
  834. Date Description Programmer
  835. -------- --------------------------------------------------- ----------
  836. 08/17/96 Initial creation. BrianAu
  837. */
  838. ///////////////////////////////////////////////////////////////////////////////
  839. INT_PTR
  840. UserPropSheet::OnHelp(
  841. HWND hDlg,
  842. WPARAM wParam,
  843. LPARAM lParam
  844. )
  845. {
  846. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE,
  847. HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgUserPropSheetHelpIDs);
  848. return TRUE;
  849. }
  850. INT_PTR
  851. UserPropSheet::OnContextMenu(
  852. HWND hwndItem,
  853. int xPos,
  854. int yPos
  855. )
  856. {
  857. int idCtl = GetDlgCtrlID(hwndItem);
  858. WinHelp(hwndItem,
  859. UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE,
  860. HELP_CONTEXTMENU,
  861. (DWORD_PTR)((LPTSTR)rgUserPropSheetHelpIDs));
  862. return FALSE;
  863. }
  864. ///////////////////////////////////////////////////////////////////////////////
  865. /* Function: UserPropPage::OnEditNotifyUpdate
  866. Description: Handler for WM_COMMAND, EN_UPDATE.
  867. Called whenever a character is entered in an edit control.
  868. Arguments:
  869. Returns: FALSE;
  870. Revision History:
  871. Date Description Programmer
  872. -------- --------------------------------------------------- ----------
  873. 09/03/96 Initial creation. BrianAu
  874. */
  875. ///////////////////////////////////////////////////////////////////////////////
  876. INT_PTR
  877. UserPropSheet::OnEditNotifyUpdate(
  878. HWND hDlg,
  879. WPARAM wParam,
  880. LPARAM lParam
  881. )
  882. {
  883. XBytes *pxb = NULL;
  884. switch(LOWORD(wParam))
  885. {
  886. case IDC_EDIT_USER_LIMIT:
  887. pxb = m_pxbQuotaLimit;
  888. break;
  889. case IDC_EDIT_USER_THRESHOLD:
  890. pxb = m_pxbQuotaThreshold;
  891. break;
  892. default:
  893. break;
  894. }
  895. if (NULL != pxb)
  896. pxb->OnEditNotifyUpdate(lParam);
  897. return FALSE;
  898. }
  899. ///////////////////////////////////////////////////////////////////////////////
  900. /* Function: UserPropSheet::OnEditNotifyKillFocus
  901. Description: Handler for WM_COMMAND, EN_KILLFOCUS.
  902. Called whenever focus leaves an edit control.
  903. Validates the value in the edit control and adjusts it if necessary.
  904. Arguments:
  905. Returns: FALSE;
  906. Revision History:
  907. Date Description Programmer
  908. -------- --------------------------------------------------- ----------
  909. 08/17/96 Initial creation. BrianAu
  910. */
  911. ///////////////////////////////////////////////////////////////////////////////
  912. INT_PTR
  913. UserPropSheet::OnEditNotifyKillFocus(
  914. HWND hDlg,
  915. WPARAM wParam,
  916. LPARAM lParam
  917. )
  918. {
  919. XBytes *pxb = NULL;
  920. switch(LOWORD(wParam))
  921. {
  922. case IDC_EDIT_USER_LIMIT:
  923. pxb = m_pxbQuotaLimit;
  924. break;
  925. case IDC_EDIT_USER_THRESHOLD:
  926. pxb = m_pxbQuotaThreshold;
  927. break;
  928. default:
  929. break;
  930. }
  931. if (NULL != pxb)
  932. pxb->OnEditKillFocus(lParam);
  933. return FALSE;
  934. }
  935. ///////////////////////////////////////////////////////////////////////////////
  936. /* Function: UserPropPage::OnComboNotifySelChange
  937. Description: Handler for WM_COMMAND, CBN_SELCHANGE.
  938. Called whenever the user selects the combo box.
  939. Arguments: Std DlgProc args.
  940. Returns: FALSE;
  941. Revision History:
  942. Date Description Programmer
  943. -------- --------------------------------------------------- ----------
  944. 09/03/96 Initial creation. BrianAu
  945. */
  946. ///////////////////////////////////////////////////////////////////////////////
  947. INT_PTR
  948. UserPropSheet::OnComboNotifySelChange(
  949. HWND hDlg,
  950. WPARAM wParam,
  951. LPARAM lParam
  952. )
  953. {
  954. XBytes *pxb = NULL;
  955. switch(LOWORD(wParam))
  956. {
  957. case IDC_CMB_USER_LIMIT:
  958. pxb = m_pxbQuotaLimit;
  959. break;
  960. case IDC_CMB_USER_THRESHOLD:
  961. pxb = m_pxbQuotaThreshold;
  962. break;
  963. default:
  964. break;
  965. }
  966. if (NULL != pxb)
  967. pxb->OnComboNotifySelChange(lParam);
  968. return FALSE;
  969. }
  970. ///////////////////////////////////////////////////////////////////////////////
  971. /* Function: UserPropSheet::ApplySettings
  972. Description: Applies the current settings to the user's quota information
  973. if they have not changed from the original settings.
  974. Arguments:
  975. hDlg - Dialog window handle.
  976. Returns:
  977. NO_ERROR - Success.
  978. E_INVALIDARG - One of the settings was invalid.
  979. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
  980. E_FAIL - Any other error.
  981. Revision History:
  982. Date Description Programmer
  983. -------- --------------------------------------------------- ----------
  984. 08/15/96 Initial creation. BrianAu
  985. 01/24/98 Added bUndo argument. BrianAu
  986. */
  987. ///////////////////////////////////////////////////////////////////////////////
  988. HRESULT
  989. UserPropSheet::ApplySettings(
  990. HWND hDlg,
  991. bool bUndo // Default == true.
  992. )
  993. {
  994. HRESULT hResult = NO_ERROR;
  995. BOOL bTranslated = FALSE;
  996. com_autoptr<DISKQUOTA_USER> ptrUser;
  997. UINT cUsers = m_LVSelection.Count();
  998. UINT i = 0;
  999. LONGLONG llThreshold;
  1000. LONGLONG llLimit;
  1001. CAutoSetRedraw autoredraw(m_hWndParent);
  1002. if (bUndo)
  1003. m_UndoList.Clear(); // Clear current undo list.
  1004. //
  1005. // Determine what threshold and limit to apply.
  1006. //
  1007. if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_USER_NOLIMIT))
  1008. {
  1009. llThreshold = NOLIMIT;
  1010. llLimit = NOLIMIT;
  1011. }
  1012. else
  1013. {
  1014. llThreshold = m_pxbQuotaThreshold->GetBytes();
  1015. llLimit = m_pxbQuotaLimit->GetBytes();
  1016. }
  1017. if (cUsers > 1)
  1018. {
  1019. //
  1020. // Create batch object and do batch update for multiple users.
  1021. //
  1022. com_autoptr<DISKQUOTA_USER_BATCH> ptrBatch;
  1023. hResult = m_pQuotaControl->CreateUserBatch(ptrBatch.getaddr());
  1024. if (SUCCEEDED(hResult))
  1025. {
  1026. for (i = 0; i < cUsers; i++)
  1027. {
  1028. m_LVSelection.Retrieve(i, ptrUser.getaddr());
  1029. if (bUndo)
  1030. {
  1031. //
  1032. // Add an entry to the undo list.
  1033. //
  1034. LONGLONG LimitUndo;
  1035. LONGLONG ThresholdUndo;
  1036. ptrUser->GetQuotaThreshold(&ThresholdUndo);
  1037. ptrUser->GetQuotaLimit(&LimitUndo);
  1038. //
  1039. // Use a local autoptr to ensure proper release of
  1040. // iface in case adding to the undo list throws an exception.
  1041. // On success, disown the real ptr so that the object
  1042. // stays with the undo list.
  1043. //
  1044. com_autoptr<DISKQUOTA_USER> ptrQuotaUser(ptrUser);
  1045. ptrUser->AddRef();
  1046. m_UndoList.Add(new UndoModify(ptrUser, ThresholdUndo, LimitUndo));
  1047. ptrQuotaUser.disown();
  1048. }
  1049. ptrUser->SetQuotaThreshold(llThreshold, FALSE);
  1050. if (UserIsAdministrator(ptrUser) && NOLIMIT != llLimit)
  1051. {
  1052. //
  1053. // User is the Administrator account AND
  1054. // We're trying to set the limit to something other than NOLIMIT.
  1055. // Can't set a limit on the administrator account.
  1056. //
  1057. DiskQuotaMsgBox(GetDesktopWindow(),
  1058. IDS_CANT_SET_ADMIN_LIMIT,
  1059. IDS_TITLE_DISK_QUOTA,
  1060. MB_ICONWARNING | MB_OK);
  1061. }
  1062. else
  1063. {
  1064. //
  1065. // OK to set quota limit.
  1066. //
  1067. ptrUser->SetQuotaLimit(llLimit, FALSE);
  1068. }
  1069. ptrBatch->Add(ptrUser);
  1070. }
  1071. hResult = ptrBatch->FlushToDisk();
  1072. }
  1073. }
  1074. else
  1075. {
  1076. //
  1077. // Do single user update or add new user.
  1078. //
  1079. m_LVSelection.Retrieve(0, ptrUser.getaddr());
  1080. DBGASSERT((NULL != ptrUser.get()));
  1081. if (bUndo)
  1082. {
  1083. //
  1084. // Add an entry to the undo list.
  1085. //
  1086. LONGLONG LimitUndo;
  1087. LONGLONG ThresholdUndo;
  1088. ptrUser->GetQuotaThreshold(&ThresholdUndo);
  1089. ptrUser->GetQuotaLimit(&LimitUndo);
  1090. //
  1091. // Use local autoptr to ensure proper release of iface ptr if
  1092. // an exception is thrown. Disown real ptr on success.
  1093. //
  1094. com_autoptr<DISKQUOTA_USER> ptrQuotaUser(ptrUser);
  1095. ptrUser->AddRef();
  1096. m_UndoList.Add(new UndoModify(ptrUser, ThresholdUndo, LimitUndo));
  1097. ptrQuotaUser.disown();
  1098. }
  1099. if (llThreshold != m_llQuotaThreshold)
  1100. {
  1101. hResult = ptrUser->SetQuotaThreshold(llThreshold, TRUE);
  1102. if (FAILED(hResult))
  1103. goto apply_failed;
  1104. m_llQuotaThreshold = llThreshold;
  1105. }
  1106. if (llLimit != m_llQuotaLimit)
  1107. {
  1108. hResult = ptrUser->SetQuotaLimit(llLimit, TRUE);
  1109. if (FAILED(hResult))
  1110. goto apply_failed;
  1111. m_llQuotaLimit = llLimit;
  1112. }
  1113. //
  1114. // Update the user's status icon and %used to reflect the new settings.
  1115. //
  1116. UpdateUserStatusIcon(hDlg,
  1117. m_llQuotaUsed,
  1118. m_llQuotaThreshold,
  1119. m_llQuotaLimit);
  1120. UpdateSpaceUsed(hDlg,
  1121. m_llQuotaUsed,
  1122. m_llQuotaLimit,
  1123. 1);
  1124. }
  1125. //
  1126. // Update the listview item(s) so the user sees a visual response to
  1127. // pressing the "Apply" button.
  1128. //
  1129. autoredraw.Set(false);
  1130. for (i = 0; i < cUsers; i++)
  1131. {
  1132. INT iItem = 0;
  1133. m_LVSelection.Retrieve(i, &iItem);
  1134. ListView_Update(m_hWndParent, iItem);
  1135. }
  1136. autoredraw.Set(true);
  1137. InvalidateRect(m_hWndParent, NULL, FALSE);
  1138. apply_failed:
  1139. return hResult;
  1140. }
  1141. ///////////////////////////////////////////////////////////////////////////////
  1142. /* Function: UserPropSheet::InitializeControls
  1143. Description: Initializes the page controls based on the user's
  1144. quota settings.
  1145. Arguments:
  1146. hDlg - Dialog window handle.
  1147. Returns:
  1148. NO_ERROR - Always returns NO_ERROR.
  1149. Revision History:
  1150. Date Description Programmer
  1151. -------- --------------------------------------------------- ----------
  1152. 08/15/96 Initial creation. BrianAu
  1153. */
  1154. ///////////////////////////////////////////////////////////////////////////////
  1155. HRESULT
  1156. UserPropSheet::InitializeControls(
  1157. HWND hDlg
  1158. )
  1159. {
  1160. PDISKQUOTA_USER pUser = NULL;
  1161. UINT cUsers = m_LVSelection.Count();
  1162. if (1 == cUsers)
  1163. {
  1164. //
  1165. // Initialize controls for a single user.
  1166. //
  1167. m_LVSelection.Retrieve(0, &pUser);
  1168. //
  1169. // Configure the Limit/NoLimit radio buttons.
  1170. // Must examine the current threshold rather than the limit because of the
  1171. // special-case for the Administrator account. That account can have a
  1172. // threshold value but quota limit must always be "No Limit".
  1173. //
  1174. CheckDlgButton(hDlg, IDC_RBN_USER_LIMIT, NOLIMIT != m_llQuotaThreshold);
  1175. CheckDlgButton(hDlg, IDC_RBN_USER_NOLIMIT, NOLIMIT == m_llQuotaThreshold);
  1176. if (UserIsAdministrator(pUser))
  1177. {
  1178. //
  1179. // Override initialization of Quota Limit control with "No Limit".
  1180. //
  1181. m_pxbQuotaLimit->SetBytes(NOLIMIT);
  1182. }
  1183. //
  1184. // Note that the XBytes controls have already been set for single-user.
  1185. // See OnInitDialog().
  1186. //
  1187. //
  1188. // Configure the remaining dialog controls.
  1189. //
  1190. UpdateUserName(hDlg, pUser);
  1191. UpdateSpaceUsed(hDlg, m_llQuotaUsed, m_llQuotaLimit, cUsers);
  1192. UpdateUserStatusIcon(hDlg,
  1193. m_llQuotaUsed,
  1194. m_llQuotaThreshold,
  1195. m_llQuotaLimit);
  1196. }
  1197. else
  1198. {
  1199. //
  1200. // Initialize controls for multiple users.
  1201. //
  1202. LONGLONG llLimit = 0;
  1203. LONGLONG llLastLimit = 0;
  1204. LONGLONG llThreshold = 0;
  1205. LONGLONG llLastThreshold = 0;
  1206. LONGLONG llUsed = 0;
  1207. LONGLONG llTotalUsed = 0;
  1208. //
  1209. // Add up the total usage by all users.
  1210. //
  1211. for (UINT i = 0; i < cUsers; i++)
  1212. {
  1213. m_LVSelection.Retrieve(i, &pUser);
  1214. pUser->GetQuotaLimit(&llLimit);
  1215. pUser->GetQuotaThreshold(&llThreshold);
  1216. pUser->GetQuotaUsed(&llUsed);
  1217. llTotalUsed += llUsed;
  1218. if (m_bHomogeneousSelection)
  1219. {
  1220. //
  1221. // Determine if at least one user has a different
  1222. // threshold or limit. If all are the same, we can display
  1223. // the values in the edit controls. Otherwise, we default
  1224. // to "No Limit". Radio buttons don't provide an
  1225. // indeterminate state like checkboxes.
  1226. //
  1227. if (i > 0 &&
  1228. (llLimit != llLastLimit ||
  1229. llThreshold != llLastThreshold))
  1230. {
  1231. m_bHomogeneousSelection = FALSE;
  1232. }
  1233. else
  1234. {
  1235. llLastLimit = llLimit;
  1236. llLastThreshold = llThreshold;
  1237. }
  1238. }
  1239. }
  1240. //
  1241. // If all selected objects have the same limit and threshold,
  1242. // set the cached data to represent multiple-selection.
  1243. // If any one is different, we stick with the volume's default
  1244. // values set in RefreshCachedQuotaInfo().
  1245. //
  1246. if (m_bHomogeneousSelection)
  1247. {
  1248. m_llQuotaThreshold = llLastThreshold;
  1249. m_llQuotaLimit = llLastLimit;
  1250. }
  1251. else
  1252. {
  1253. //
  1254. // Since not all selected users have the same limit/thresold,
  1255. // the number we're displaying will be a change for at least
  1256. // one user. Activate the "Apply" button.
  1257. //
  1258. //
  1259. PostMessage(hDlg, DQM_ENABLE_APPLY_BUTTON, 0, 0);
  1260. }
  1261. m_pxbQuotaThreshold->SetBytes(m_llQuotaThreshold);
  1262. m_pxbQuotaLimit->SetBytes(m_llQuotaLimit);
  1263. //
  1264. // Configure the Limit/NoLimit radio buttons.
  1265. //
  1266. CheckDlgButton(hDlg,
  1267. IDC_RBN_USER_NOLIMIT,
  1268. NOLIMIT == m_llQuotaThreshold);
  1269. CheckDlgButton(hDlg,
  1270. IDC_RBN_USER_LIMIT,
  1271. NOLIMIT != m_llQuotaThreshold);
  1272. UpdateUserName(hDlg, cUsers);
  1273. UpdateSpaceUsed(hDlg, llTotalUsed, NOLIMIT, cUsers);
  1274. //
  1275. // Don't display any user status icon for multi-users.
  1276. //
  1277. }
  1278. //
  1279. // If "No Limit" radio button is checked, set limit and threshold controls
  1280. // to the "No Limit" state (disabled and displaying "No Limit" text).
  1281. // This may override any setting we made above.
  1282. //
  1283. if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_USER_NOLIMIT))
  1284. {
  1285. m_pxbQuotaThreshold->SetBytes(NOLIMIT);
  1286. m_pxbQuotaLimit->SetBytes(NOLIMIT);
  1287. }
  1288. //
  1289. // Set user icon.
  1290. //
  1291. SendMessage(GetDlgItem(hDlg, IDC_ICON_USER),
  1292. STM_SETICON,
  1293. (WPARAM)m_hIconUser[1 == cUsers ? iICON_USER_SINGLE :
  1294. iICON_USER_MULTIPLE],
  1295. 0);
  1296. //
  1297. // Force the property sheet to disable the "Apply" button.
  1298. // The way I have set up the "Apply" enabling logic through OnCommand(),
  1299. // merely initializing the edit controls on the page causes the Apply
  1300. // button to become enabled. Since the user hasn't changed anything
  1301. // yet, it should be disabled.
  1302. //
  1303. m_bIsDirty = FALSE;
  1304. PropSheet_UnChanged(GetParent(hDlg), hDlg);
  1305. return NO_ERROR;
  1306. }
  1307. ///////////////////////////////////////////////////////////////////////////////
  1308. /* Function: UserPropSheet::QueryUserStatusIcon
  1309. Description: This function is provided for automated testing of the UI.
  1310. It is used by test scripts to determine which user status icon is
  1311. currently displayed.
  1312. Arguments:
  1313. hDlg - Dialog handle.
  1314. Returns: -1 = No icon displayed.
  1315. 0 = "Everything OK" icon.
  1316. 1 = Threshold exceded icon.
  1317. 2 = Limit exceded icon.
  1318. Revision History:
  1319. Date Description Programmer
  1320. -------- --------------------------------------------------- ----------
  1321. 08/15/96 Initial creation. BrianAu
  1322. */
  1323. ///////////////////////////////////////////////////////////////////////////////
  1324. INT
  1325. UserPropSheet::QueryUserStatusIcon(
  1326. HWND hDlg
  1327. ) const
  1328. {
  1329. HICON hicon = (HICON)SendMessage(GetDlgItem(hDlg, IDC_ICON_USERSTATUS),
  1330. STM_GETICON,
  1331. 0, 0);
  1332. for (UINT i = 0; i < cSTATUS_ICONS; i++)
  1333. {
  1334. if (hicon == m_hIconStatus[i])
  1335. return i;
  1336. }
  1337. return -1;
  1338. }
  1339. ///////////////////////////////////////////////////////////////////////////////
  1340. /* Function: UserPropSheet::QueryUserIcon
  1341. Description: This function is provided for automated testing of the UI.
  1342. It is used by test scripts to determine which user status icon is
  1343. currently displayed.
  1344. Arguments:
  1345. hDlg - Dialog handle.
  1346. Returns: -1 = No icon displayed.
  1347. 0 = Single-user icon.
  1348. 1 = Multi-user icon.
  1349. Revision History:
  1350. Date Description Programmer
  1351. -------- --------------------------------------------------- ----------
  1352. 08/15/96 Initial creation. BrianAu
  1353. */
  1354. ///////////////////////////////////////////////////////////////////////////////
  1355. INT
  1356. UserPropSheet::QueryUserIcon(
  1357. HWND hDlg
  1358. ) const
  1359. {
  1360. HICON hicon = (HICON)SendMessage(GetDlgItem(hDlg, IDC_ICON_USER),
  1361. STM_GETICON,
  1362. 0, 0);
  1363. for (UINT i = 0; i < cUSER_ICONS; i++)
  1364. {
  1365. if (hicon == m_hIconUser[i])
  1366. return i;
  1367. }
  1368. return -1;
  1369. }
  1370. ///////////////////////////////////////////////////////////////////////////////
  1371. /* Function: UserPropSheet::UpdateUserStatusIcon
  1372. Description: Updates the quota status icon in the dialog box. This icon
  1373. must match the icon displayed in the listview for the selected user.
  1374. Arguments:
  1375. hDlg - Dialog handle.
  1376. iUsed - Quota bytes charged to user.
  1377. iThreshold - Quota warning threshold (bytes).
  1378. iLimit - User's quota limit.
  1379. Returns: Nothing.
  1380. Revision History:
  1381. Date Description Programmer
  1382. -------- --------------------------------------------------- ----------
  1383. 08/15/96 Initial creation. BrianAu
  1384. */
  1385. ///////////////////////////////////////////////////////////////////////////////
  1386. VOID
  1387. UserPropSheet::UpdateUserStatusIcon(
  1388. HWND hDlg,
  1389. LONGLONG iUsed,
  1390. LONGLONG iThreshold,
  1391. LONGLONG iLimit
  1392. )
  1393. {
  1394. //
  1395. // Set the user status icon if user is exceding the
  1396. // quota threshold or the limit. This is the same icon that is
  1397. // displayed in the listview status column. This logic must
  1398. // mirror that used in DetailsView::GetDispInfo_Image().
  1399. //
  1400. INT iIcon = iICON_STATUS_OK;
  1401. if (NOLIMIT != iLimit && iUsed > iLimit)
  1402. {
  1403. iIcon = iICON_STATUS_OVER_LIMIT;
  1404. }
  1405. else if (NOLIMIT != iThreshold && iUsed > iThreshold)
  1406. {
  1407. iIcon = iICON_STATUS_OVER_THRESHOLD;
  1408. }
  1409. SendMessage(GetDlgItem(hDlg, IDC_ICON_USERSTATUS),
  1410. STM_SETICON,
  1411. (WPARAM)m_hIconStatus[iIcon],
  1412. 0);
  1413. }
  1414. ///////////////////////////////////////////////////////////////////////////////
  1415. /* Function: UserPropSheet::UpdateUserName
  1416. Description: Updates the Domain\Name text with the user's domain name
  1417. and account name strings. This method is called for a single-user
  1418. selection.
  1419. Also sets the property sheet title text.
  1420. Arguments:
  1421. hDlg - Dialog handle.
  1422. pUser - Address of user's IDiskQuotaUser interface.
  1423. Returns: Nothing.
  1424. Revision History:
  1425. Date Description Programmer
  1426. -------- --------------------------------------------------- ----------
  1427. 08/15/96 Initial creation. BrianAu
  1428. 08/05/97 Added code to set prop sheet title text. BrianAu
  1429. */
  1430. ///////////////////////////////////////////////////////////////////////////////
  1431. VOID
  1432. UserPropSheet::UpdateUserName(
  1433. HWND hDlg,
  1434. PDISKQUOTA_USER pUser
  1435. )
  1436. {
  1437. DBGASSERT((NULL != pUser));
  1438. //
  1439. // Display the user name, or some status text
  1440. // if the name hasn't been resolved.
  1441. //
  1442. CString strLogonName;
  1443. DWORD dwAccountStatus = 0;
  1444. pUser->GetAccountStatus(&dwAccountStatus);
  1445. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
  1446. {
  1447. //
  1448. // User account name has been resolved. Display it.
  1449. //
  1450. TCHAR szLogonName[MAX_USERNAME];
  1451. TCHAR szDisplayName[MAX_FULL_USERNAME];
  1452. pUser->GetName(NULL, 0,
  1453. szLogonName, ARRAYSIZE(szLogonName),
  1454. szDisplayName, ARRAYSIZE(szDisplayName));
  1455. if (TEXT('\0') != szLogonName[0])
  1456. {
  1457. if (TEXT('\0') != szDisplayName[0])
  1458. {
  1459. strLogonName.Format(g_hInstDll,
  1460. IDS_FMT_DISPLAY_LOGON,
  1461. szDisplayName,
  1462. szLogonName);
  1463. }
  1464. else
  1465. {
  1466. strLogonName = szLogonName;
  1467. }
  1468. }
  1469. }
  1470. else
  1471. {
  1472. //
  1473. // User account name has not been resolved or cannot
  1474. // be resolved for some reason. Display appropriate
  1475. // status text. This is the same text displayed in the
  1476. // listview when the user's name has not been resolved.
  1477. //
  1478. INT idText = IDS_USER_ACCOUNT_UNKNOWN;
  1479. switch(dwAccountStatus)
  1480. {
  1481. case DISKQUOTA_USER_ACCOUNT_UNAVAILABLE:
  1482. idText = IDS_USER_ACCOUNT_UNAVAILABLE;
  1483. break;
  1484. case DISKQUOTA_USER_ACCOUNT_DELETED:
  1485. idText = IDS_USER_ACCOUNT_DELETED;
  1486. break;
  1487. case DISKQUOTA_USER_ACCOUNT_INVALID:
  1488. idText = IDS_USER_ACCOUNT_INVALID;
  1489. break;
  1490. case DISKQUOTA_USER_ACCOUNT_UNRESOLVED:
  1491. idText = IDS_USER_ACCOUNT_UNRESOLVED;
  1492. break;
  1493. case DISKQUOTA_USER_ACCOUNT_UNKNOWN:
  1494. default:
  1495. break;
  1496. }
  1497. strLogonName.Format(g_hInstDll, idText);
  1498. }
  1499. SetDlgItemText(hDlg, IDC_TXT_USERNAME, strLogonName);
  1500. //
  1501. // Format and draw the prop sheet title string.
  1502. //
  1503. CString strSheetTitle(g_hInstDll,
  1504. IDS_TITLE_EDIT_USER,
  1505. (LPCTSTR)strLogonName);
  1506. PropSheet_SetTitle(GetParent(hDlg), 0, (LPCTSTR)strSheetTitle);
  1507. }
  1508. ///////////////////////////////////////////////////////////////////////////////
  1509. /* Function: UserPropSheet::UpdateUserName
  1510. Description: Replaces the user Domain\Name text with a message showing
  1511. how many users are selected. This is used for multi-user selections
  1512. where no single user name is applicable.
  1513. Also sets the property sheet title text.
  1514. Arguments:
  1515. hDlg - Dialog handle.
  1516. cUsers - Number of users represented in the property dialog.
  1517. Returns: Nothing.
  1518. Revision History:
  1519. Date Description Programmer
  1520. -------- --------------------------------------------------- ----------
  1521. 08/15/96 Initial creation. BrianAu
  1522. 08/05/97 Added code to set prop sheet title text. BrianAu
  1523. */
  1524. ///////////////////////////////////////////////////////////////////////////////
  1525. VOID
  1526. UserPropSheet::UpdateUserName(
  1527. HWND hDlg,
  1528. INT cUsers
  1529. )
  1530. {
  1531. //
  1532. // Hide name edit control. Can't display names for all users.
  1533. // Display "Multiple Quota Users." instead.
  1534. //
  1535. CString strText(g_hInstDll, IDS_TITLE_MULTIUSER, cUsers);
  1536. SetDlgItemText(hDlg, IDC_TXT_USERNAME, strText);
  1537. //
  1538. // Set the title of the property sheet.
  1539. //
  1540. CString strSheetTitle(g_hInstDll, IDS_TITLE_EDIT_MULTIUSER);
  1541. PropSheet_SetTitle(GetParent(hDlg), 0, (LPCTSTR)strSheetTitle);
  1542. }
  1543. ///////////////////////////////////////////////////////////////////////////////
  1544. /* Function: UserPropSheet::UpdateSpaceUsed
  1545. Description: Updates the "space used" and "remaining" fields on the user
  1546. property sheet.
  1547. Arguments:
  1548. hDlg - Dialog handle.
  1549. iUsed - Quota bytes charged to user(s).
  1550. iLimit - User's quota limit.
  1551. cUsers - Number of users represented in the property dialog.
  1552. Returns: Nothing.
  1553. Revision History:
  1554. Date Description Programmer
  1555. -------- --------------------------------------------------- ----------
  1556. 08/15/96 Initial creation. BrianAu
  1557. */
  1558. ///////////////////////////////////////////////////////////////////////////////
  1559. VOID
  1560. UserPropSheet::UpdateSpaceUsed(
  1561. HWND hDlg,
  1562. LONGLONG iUsed,
  1563. LONGLONG iLimit,
  1564. INT cUsers
  1565. )
  1566. {
  1567. TCHAR szText[80];
  1568. //
  1569. // Display - Used: 999XB (99%)
  1570. //
  1571. XBytes::FormatByteCountForDisplay(iUsed,
  1572. szText, ARRAYSIZE(szText));
  1573. CString strText(szText);
  1574. if (1 == cUsers)
  1575. {
  1576. //
  1577. // Only single-user page gets (99%) appended.
  1578. // Pct quota is meaningless for multiple users.
  1579. //
  1580. if (0 != iLimit && NOLIMIT != iLimit)
  1581. {
  1582. UINT iPct = (INT)((iUsed * 100) / iLimit);
  1583. strText.Format(g_hInstDll,
  1584. IDS_QUOTA_USED_SINGLEUSER,
  1585. szText,
  1586. iPct);
  1587. }
  1588. }
  1589. SetDlgItemText(hDlg,
  1590. IDC_TXT_SPACEUSED,
  1591. strText);
  1592. //
  1593. // Display - Remaining: 999XB
  1594. //
  1595. strText = szText;
  1596. if (NOLIMIT != iLimit)
  1597. {
  1598. LONGLONG iAmount = 0;
  1599. if (iUsed <= iLimit)
  1600. iAmount = iLimit - iUsed;
  1601. XBytes::FormatByteCountForDisplay(iAmount,
  1602. strText.GetBuffer(80), 80);
  1603. strText.ReleaseBuffer();
  1604. }
  1605. else
  1606. {
  1607. //
  1608. // Display "N/A" if limit is NOLIMIT.
  1609. //
  1610. strText.Format(g_hInstDll, IDS_NOT_APPLICABLE);
  1611. }
  1612. SetDlgItemText(hDlg,
  1613. IDC_TXT_SPACEREMAINING,
  1614. strText);
  1615. }