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.

10384 lines
322 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: details.cpp
  3. Description: Contains definition for class DetailsView.
  4. This class implements a list view containing quota information about
  5. the various accounts in a volume's quota information file.
  6. Revision History:
  7. Date Description Programmer
  8. -------- --------------------------------------------------- ----------
  9. 08/20/96 Initial creation. BrianAu
  10. 05/28/97 Major changes. BrianAu
  11. - Added "User Finder".
  12. - Added promotion of selected item to front of
  13. name resolution queue.
  14. - Improved name resolution status reporting through
  15. listview.
  16. - Moved drag/drop and report generation code
  17. from dragdrop.cpp and reptgen.cpp into the
  18. DetailsView class. DetailsView now implements
  19. IDataObject, IDropSource and IDropTarget instead
  20. of deferring implementation to secondary objects.
  21. dragdrop.cpp and reptgen.cpp have been dropped
  22. from the project.
  23. - Added support for CF_HDROP and private import/
  24. export clipboard formats.
  25. - Added import/export functionality.
  26. 07/28/97 Removed export support for CF_HDROP. Replaced BrianAu
  27. with FileContents and FileGroupDescriptor. Import
  28. from CF_HDROP is still supported.
  29. Added Import Source object hierarchy.
  30. */
  31. ///////////////////////////////////////////////////////////////////////////////
  32. #include "pch.h" // PCH
  33. #pragma hdrstop
  34. #include <htmlhelp.h>
  35. #include <commctrl.h>
  36. #include <commdlg.h>
  37. #include "uihelp.h"
  38. #include "uiutils.h"
  39. #include "dskquota.h"
  40. #include "registry.h"
  41. #include "resource.h"
  42. #include "shellinc.h"
  43. #include "userprop.h"
  44. #include "details.h"
  45. #include "guidsp.h"
  46. #include "mapfile.h"
  47. #include "progress.h"
  48. #include "yntoall.h"
  49. #include "ownerlst.h"
  50. #include "ownerdlg.h"
  51. #include "adusrdlg.h"
  52. //
  53. // Constant text strings.
  54. //
  55. TCHAR c_szWndClassDetailsView[] = TEXT("DetailsView");
  56. //
  57. // Bitmap dimension constants.
  58. //
  59. const UINT BITMAP_WIDTH = 16;
  60. const UINT BITMAP_HEIGHT = 16;
  61. const UINT LG_BITMAP_WIDTH = 32;
  62. const UINT LG_BITMAP_HEIGHT = 32;
  63. //
  64. // How much to grow the user object list whenever expansion is required.
  65. //
  66. const INT USER_LIST_GROW_AMT = 100;
  67. //
  68. // This structure is used to pass the DetailsView object's "this" pointer
  69. // in WM_CREATE.
  70. //
  71. typedef struct WndCreationData {
  72. SHORT cbExtra;
  73. LPVOID pThis;
  74. } WNDCREATE_DATA;
  75. typedef UNALIGNED WNDCREATE_DATA *PWNDCREATE_DATA;
  76. //
  77. // Structure passed to CompareItems callback.
  78. //
  79. typedef struct comparestruct
  80. {
  81. DWORD idColumn;
  82. DWORD dwDirection;
  83. DetailsView *pThis;
  84. } COMPARESTRUCT, *PCOMPARESTRUCT;
  85. //
  86. // Define some names for indexes into the listview's image list.
  87. //
  88. #define iIMAGELIST_ICON_NOIMAGE (-1)
  89. #define iIMAGELIST_ICON_OK 0
  90. #define iIMAGELIST_ICON_WARNING 1
  91. #define iIMAGELIST_ICON_LIMIT 2
  92. //
  93. // The 0-based index of the "View" item in the main menu and of the
  94. // "Arrange" item in the view menu.
  95. // WARNING: If you change menu items, these may need updating.
  96. //
  97. #define iMENUITEM_VIEW 2
  98. #define iMENUITEM_VIEW_ARRANGE 4
  99. //
  100. // Same thing for the "Edit" menu.
  101. //
  102. #define iMENUITEM_EDIT 1
  103. //
  104. // Add/remove from this array to change the columns in the list view.
  105. // IMPORTANT:
  106. // The ordering of these items is very important (sort of).
  107. // Because of a bug in commctrl.h, they don't paint under the bitmap
  108. // if it's the only thing in the column (or if it's the bitmap of the primary
  109. // item). Also, the behavior of the listview is such that the text in
  110. // subitem 0 is always shifted right the width of a small bitmap. When
  111. // I had the status column NOT as item 0, there were two display problems.
  112. // 1) First, the text in column 0 was always shifted right to allow for the
  113. // bitmap we weren't using. This looked funny.
  114. // 2) The full-row-select highlight didn't properly paint the background
  115. // of the status bitmap.
  116. //
  117. // By placing the status column as subitem 0, we eliminate problem 1 since
  118. // we're using a bitmap in subitem 0 (listview's default behavior).
  119. // If we drag the status column out of the leftmost position, they still don't
  120. // paint under the bitmap but at least it will work like any other explorer
  121. // view. When/if they fix listview, we'll be fixed automatically.
  122. //
  123. const DV_COLDATA g_rgColumns[] = {
  124. { LVCFMT_LEFT |
  125. LVCFMT_COL_HAS_IMAGES,
  126. 0, IDS_TITLE_COL_STATUS, DetailsView::idCol_Status },
  127. { LVCFMT_LEFT, 0, IDS_TITLE_COL_FOLDER, DetailsView::idCol_Folder },
  128. { LVCFMT_LEFT, 0, IDS_TITLE_COL_USERNAME, DetailsView::idCol_Name },
  129. { LVCFMT_LEFT, 0, IDS_TITLE_COL_LOGONNAME, DetailsView::idCol_LogonName },
  130. { LVCFMT_RIGHT, 0, IDS_TITLE_COL_AMTUSED, DetailsView::idCol_AmtUsed },
  131. { LVCFMT_RIGHT, 0, IDS_TITLE_COL_LIMIT, DetailsView::idCol_Limit },
  132. { LVCFMT_RIGHT, 0, IDS_TITLE_COL_THRESHOLD, DetailsView::idCol_Threshold },
  133. { LVCFMT_RIGHT, 0, IDS_TITLE_COL_PCTUSED, DetailsView::idCol_PctUsed },
  134. };
  135. //
  136. // User quota state constants.
  137. // used for identifying which icon to display in "Status" column.
  138. //
  139. const INT iUSERSTATE_OK = 0;
  140. const INT iUSERSTATE_WARNING = 1;
  141. const INT iUSERSTATE_OVERLIMIT = 2;
  142. //
  143. // Maximum number of entries allowed in the "Find User" MRU list.
  144. //
  145. const INT DetailsView::MAX_FINDMRU_ENTRIES = 10;
  146. //
  147. // Dimensions for the "Find User" combo box in the toolbar.
  148. //
  149. const INT DetailsView::CX_TOOLBAR_COMBO = 200;
  150. const INT DetailsView::CY_TOOLBAR_COMBO = 200;
  151. ///////////////////////////////////////////////////////////////////////////////
  152. /* Function: DetailsView::DetailsView
  153. Description: Class constructor.
  154. Arguments: None.
  155. Returns: Nothing.
  156. Exceptions: OutOfMemory
  157. Revision History:
  158. Date Description Programmer
  159. -------- --------------------------------------------------- ----------
  160. 08/20/96 Initial creation. BrianAu
  161. 02/21/97 Ownerdata listview. Added m_UserList. BrianAu
  162. */
  163. ///////////////////////////////////////////////////////////////////////////////
  164. DetailsView::DetailsView(
  165. VOID
  166. ) : m_cRef(0),
  167. m_UserList(USER_LIST_GROW_AMT),
  168. m_hwndMain(NULL),
  169. m_hwndListView(NULL),
  170. m_hwndStatusBar(NULL),
  171. m_hwndToolBar(NULL),
  172. m_hwndListViewToolTip(NULL),
  173. m_hwndHeader(NULL),
  174. m_hKbdAccel(NULL),
  175. m_lpfnLVWndProc(NULL),
  176. m_pQuotaControl(NULL),
  177. m_pUserFinder(NULL),
  178. m_DropSource(MK_LBUTTON),
  179. m_DropTarget(MK_LBUTTON),
  180. m_pDataObject(NULL),
  181. m_pUndoList(NULL),
  182. m_ColMap(ARRAYSIZE(g_rgColumns)),
  183. m_strAccountUnresolved(g_hInstDll, IDS_USER_ACCOUNT_UNRESOLVED),
  184. m_strAccountUnavailable(g_hInstDll, IDS_USER_ACCOUNT_UNAVAILABLE),
  185. m_strAccountUnknown(g_hInstDll, IDS_USER_ACCOUNT_UNKNOWN),
  186. m_strAccountDeleted(g_hInstDll, IDS_USER_ACCOUNT_DELETED),
  187. m_strAccountInvalid(g_hInstDll, IDS_USER_ACCOUNT_INVALID),
  188. m_strNoLimit(g_hInstDll, IDS_NO_LIMIT),
  189. m_strNotApplicable(g_hInstDll, IDS_NOT_APPLICABLE),
  190. m_strStatusOK(g_hInstDll, IDS_STATUS_OK),
  191. m_strStatusWarning(g_hInstDll, IDS_STATUS_WARNING),
  192. m_strStatusOverlimit(g_hInstDll, IDS_STATUS_OVERLIMIT),
  193. m_pIDataObjectOnClipboard(NULL),
  194. m_dwEventCookie(0),
  195. m_iLastItemHit(-1),
  196. m_iLastColSorted(-1),
  197. m_fSortDirection(0),
  198. m_bMenuActive(FALSE),
  199. m_bWaitCursor(FALSE),
  200. m_bStopLoadingObjects(FALSE),
  201. m_bDestroyingView(FALSE)
  202. {
  203. DBGTRACE((DM_VIEW, DL_HIGH, TEXT("DetailsView::DetailsView")));
  204. //
  205. // Make sure the idCol_XXX constants agree
  206. // with the size of g_rgColumns.
  207. //
  208. DBGASSERT((ARRAYSIZE(g_rgColumns) == DetailsView::idCol_Last));
  209. ZeroMemory(&m_lvsi, sizeof(m_lvsi));
  210. m_ptMouse.x = 0;
  211. m_ptMouse.y = 0;
  212. InitializeCriticalSection(&m_csAsyncUpdate);
  213. }
  214. ///////////////////////////////////////////////////////////////////////////////
  215. /* Function: DetailsView::Initialize
  216. Description: Initializes a new details view object.
  217. Arguments:
  218. idVolume - Ref to a const CVolumeID object containing both the
  219. parsable and displayable names for the volume.
  220. Returns: TRUE = Success.
  221. FALSE = Out of memory or couldn't create thread.
  222. Either way, we can't run the view.
  223. Revision History:
  224. Date Description Programmer
  225. -------- --------------------------------------------------- ----------
  226. 12/06/96 Initial creation. Moved this code out of the ctor. BrianAu
  227. 02/25/97 Removed m_hwndPropPage from DetailsView. BrianAu
  228. 05/20/97 Added user finder object. BrianAu
  229. 06/28/98 Added support for mounted volumes. BrianAu
  230. */
  231. ///////////////////////////////////////////////////////////////////////////////
  232. BOOL
  233. DetailsView::Initialize(
  234. const CVolumeID& idVolume
  235. )
  236. {
  237. BOOL bResult = FALSE;
  238. HANDLE hThread = NULL;
  239. try
  240. {
  241. //
  242. // Create the reg parameter objects we'll be using in the UI.
  243. // The RegParamTable functions will not add a duplicate entry.
  244. //
  245. //
  246. // Parameter: Preferences
  247. //
  248. LV_STATE_INFO lvsi;
  249. InitLVStateInfo(&lvsi);
  250. //
  251. // Create a private copy of the file sys object name string.
  252. // This can throw OutOfMemory.
  253. //
  254. m_idVolume = idVolume;
  255. if (FAILED(CreateVolumeDisplayName(m_idVolume, &m_strVolumeDisplayName)))
  256. {
  257. m_strVolumeDisplayName = m_idVolume.ForDisplay();
  258. }
  259. //
  260. // Read saved state of listview from registry.
  261. // Saved info includes window ht/wd, column widths and
  262. // toolbar/status bar visibility. Need this info before we start thread.
  263. //
  264. RegKey keyPref(HKEY_CURRENT_USER, REGSTR_KEY_DISKQUOTA);
  265. if (FAILED(keyPref.Open(KEY_READ)) ||
  266. FAILED(keyPref.GetValue(REGSTR_VAL_PREFERENCES, (LPBYTE)&m_lvsi, sizeof(m_lvsi))) ||
  267. !DetailsView::IsValidLVStateInfo(&m_lvsi))
  268. {
  269. //
  270. // Protect us from truly bogus data. If it's bad, or obsolete,
  271. // just re-initialize it.
  272. //
  273. DBGERROR((TEXT("Listview persist state info invalid. Re-initializing.")));
  274. DetailsView::InitLVStateInfo(&m_lvsi);
  275. }
  276. //
  277. // Transfer sorting information to member variables.
  278. // These can be changed by user-initiated events.
  279. //
  280. m_iLastColSorted = m_lvsi.iLastColSorted;
  281. m_fSortDirection = m_lvsi.fSortDirection;
  282. //
  283. // Create the user finder object.
  284. // This is used to locate users through the toolbar combo box and
  285. // the "Find User" dialog. The finder object maintains a MRU list for
  286. // both the toolbar and dialog combos.
  287. //
  288. m_pUserFinder = new Finder(*this, MAX_FINDMRU_ENTRIES);
  289. //
  290. // Create the data object we use to control data transfers.
  291. //
  292. m_pDataObject = new DataObject(*this);
  293. //
  294. // Create a new thread on which to run the details view window.
  295. // This is so that the details view will remain alive if the
  296. // property page is destroyed. This must be done last in this method
  297. // so that if we return FALSE, the caller is assured there is no thread
  298. // running loose. If we return FALSE, they'll have to call "delete"
  299. // to release any string allocations done above. If we return TRUE,
  300. // the caller must not call delete on the object. The object will
  301. // destroy itself when the user closes the view window.
  302. //
  303. hThread = CreateThread(NULL, // No security attributes.
  304. 0, // Default stack size.
  305. &ThreadProc,
  306. this, // Static thread proc needs this.
  307. 0, // Not suspended.
  308. NULL);
  309. if (NULL != hThread)
  310. {
  311. CloseHandle(hThread);
  312. //
  313. // Everything succeeded.
  314. //
  315. bResult = TRUE;
  316. }
  317. }
  318. catch(CAllocException& e)
  319. {
  320. //
  321. // Catch an allocation exception here.
  322. // We'll return FALSE indicating initialization failure.
  323. //
  324. }
  325. return bResult;
  326. }
  327. ///////////////////////////////////////////////////////////////////////////////
  328. /* Function: DetailsView::~DetailsView
  329. Description: Class destructor.
  330. Arguments: None.
  331. Returns: Nothing.
  332. Revision History:
  333. Date Description Programmer
  334. -------- --------------------------------------------------- ----------
  335. 08/20/96 Initial creation. BrianAu
  336. 02/21/97 Ownerdata listview. Added m_UserList. BrianAu
  337. 05/20/97 Added user finder object. BrianAu
  338. */
  339. ///////////////////////////////////////////////////////////////////////////////
  340. DetailsView::~DetailsView(
  341. VOID
  342. )
  343. {
  344. DBGTRACE((DM_VIEW, DL_HIGH, TEXT("DetailsView::~DetailsView")));
  345. //
  346. // Destroy the user object list if it still has some objects.
  347. //
  348. ReleaseObjects();
  349. delete m_pUserFinder;
  350. delete m_pUndoList;
  351. delete m_pDataObject;
  352. DeleteCriticalSection(&m_csAsyncUpdate);
  353. }
  354. ///////////////////////////////////////////////////////////////////////////////
  355. /* Function: DetailsView::QueryInterface
  356. Description: Returns an interface pointer to the object's supported
  357. interfaces.
  358. Arguments:
  359. riid - Reference to requested interface ID.
  360. ppvOut - Address of interface pointer variable to accept interface ptr.
  361. Returns:
  362. NO_ERROR - Success.
  363. E_NOINTERFACE - Requested interface not supported.
  364. E_INVALIDARG - ppvOut argument was NULL.
  365. Revision History:
  366. Date Description Programmer
  367. -------- --------------------------------------------------- ----------
  368. 08/20/96 Initial creation. BrianAu
  369. */
  370. ///////////////////////////////////////////////////////////////////////////////
  371. STDMETHODIMP
  372. DetailsView::QueryInterface(
  373. REFIID riid,
  374. LPVOID *ppv
  375. )
  376. {
  377. HRESULT hResult = NO_ERROR;
  378. if (NULL != ppv)
  379. {
  380. *ppv = NULL;
  381. if (IID_IUnknown == riid || IID_IDiskQuotaEvents == riid)
  382. {
  383. *ppv = static_cast<IDiskQuotaEvents *>(this);
  384. }
  385. else if (IID_IDataObject == riid)
  386. {
  387. *ppv = static_cast<IDataObject *>(this);
  388. }
  389. else if (IID_IDropSource == riid)
  390. {
  391. *ppv = static_cast<IDropSource *>(this);
  392. }
  393. else if (IID_IDropTarget == riid)
  394. {
  395. *ppv = static_cast<IDropTarget *>(this);
  396. }
  397. else
  398. hResult = E_NOINTERFACE;
  399. if (NULL != *ppv)
  400. {
  401. ((LPUNKNOWN)*ppv)->AddRef();
  402. }
  403. }
  404. else
  405. hResult = E_INVALIDARG;
  406. return hResult;
  407. }
  408. ///////////////////////////////////////////////////////////////////////////////
  409. /* Function: DetailsView::AddRef
  410. Description: Increments object reference count.
  411. Arguments: None.
  412. Returns: New reference count value.
  413. Revision History:
  414. Date Description Programmer
  415. -------- --------------------------------------------------- ----------
  416. 08/15/96 Initial creation. BrianAu
  417. */
  418. ///////////////////////////////////////////////////////////////////////////////
  419. STDMETHODIMP_(ULONG)
  420. DetailsView::AddRef(
  421. VOID
  422. )
  423. {
  424. ULONG ulReturn = m_cRef + 1;
  425. DBGPRINT((DM_COM, DL_HIGH, TEXT("DetailsView::AddRef, 0x%08X %d -> %d\n"),
  426. this, ulReturn - 1, ulReturn));
  427. InterlockedIncrement(&m_cRef);
  428. return ulReturn;
  429. }
  430. ///////////////////////////////////////////////////////////////////////////////
  431. /* Function: DetailsView::Release
  432. Description: Decrements object reference count. If count drops to 0,
  433. object is deleted.
  434. Arguments: None.
  435. Returns: New reference count value.
  436. Revision History:
  437. Date Description Programmer
  438. -------- --------------------------------------------------- ----------
  439. 08/15/96 Initial creation. BrianAu
  440. */
  441. ///////////////////////////////////////////////////////////////////////////////
  442. STDMETHODIMP_(ULONG)
  443. DetailsView::Release(
  444. VOID
  445. )
  446. {
  447. ULONG ulReturn = m_cRef - 1;
  448. DBGPRINT((DM_COM, DL_HIGH, TEXT("DetailsView::Release, 0x%08X %d -> %d\n"),
  449. this, ulReturn + 1, ulReturn));
  450. if (InterlockedDecrement(&m_cRef) == 0)
  451. {
  452. delete this;
  453. ulReturn = 0;
  454. }
  455. return ulReturn;
  456. }
  457. ///////////////////////////////////////////////////////////////////////////////
  458. /* Function: DetailsView::ThreadProc
  459. Description: Thread procedure for the details view window. Creates the
  460. quota control object and the main window. Then it just sits
  461. processing messages until it receives a WM_QUIT message.
  462. Arguments:
  463. pvParam - Address of DetailsView instance.
  464. Returns:
  465. Always returns 0.
  466. Revision History:
  467. Date Description Programmer
  468. -------- --------------------------------------------------- ----------
  469. 08/20/96 Initial creation. BrianAu
  470. 03/22/00 Fixed proc param for ia64. BrianAu
  471. */
  472. ///////////////////////////////////////////////////////////////////////////////
  473. DWORD
  474. DetailsView::ThreadProc(
  475. LPVOID pvParam
  476. )
  477. {
  478. HRESULT hResult = NO_ERROR;
  479. DetailsView *pThis = (DetailsView *)pvParam;
  480. DBGPRINT((DM_VIEW, DL_HIGH, TEXT("LISTVIEW - New thread %d"), GetCurrentThreadId()));
  481. DBGASSERT((NULL != pThis));
  482. //
  483. // Need to ensure DLL stays loaded while this thread is active.
  484. //
  485. InterlockedIncrement(&g_cRefThisDll);
  486. //
  487. // This will keep the view object alive while the thread is alive.
  488. // We call Release when the thread terminates.
  489. //
  490. pThis->AddRef();
  491. //
  492. // Must call OleInitialize() for new thread.
  493. //
  494. try
  495. {
  496. if (SUCCEEDED(OleInitialize(NULL)))
  497. {
  498. //
  499. // Create the quota control object.
  500. // Why don't we just use the same quota controller as the
  501. // volume property page? Good question.
  502. // Since we're on a separate thread, we either need a new
  503. // object or marshal the IDiskQuotaControl interface.
  504. // I chose to create a new object rather than take the
  505. // performance hit of the additional marshaling. The quota
  506. // controller object is used heavily by the details view.
  507. //
  508. hResult = CoCreateInstance(CLSID_DiskQuotaControl,
  509. NULL,
  510. CLSCTX_INPROC_SERVER,
  511. IID_IDiskQuotaControl,
  512. (LPVOID *)&(pThis->m_pQuotaControl));
  513. if (SUCCEEDED(hResult))
  514. {
  515. hResult = pThis->m_pQuotaControl->Initialize(pThis->m_idVolume.ForParsing(),
  516. TRUE); // Read-write.
  517. if (SUCCEEDED(hResult))
  518. {
  519. //
  520. // Create the main window.
  521. //
  522. hResult = pThis->CreateMainWindow();
  523. if (SUCCEEDED(hResult))
  524. {
  525. MSG msg;
  526. DBGASSERT((NULL != pThis->m_hwndMain));
  527. //
  528. // Place a message in the queue that the window has been
  529. // created. Now creation of the other controls can procede.
  530. //
  531. // It is VERY important that once we receive a WM_QUIT message,
  532. // no members of the DetailsView instance are referenced.
  533. // Posting WM_QUIT is the last thing done by the WM_DESTROY handler.
  534. //
  535. PostMessage(pThis->m_hwndMain, WM_MAINWINDOW_CREATED, 0, 0);
  536. while (0 != GetMessage(&msg, NULL, 0, 0))
  537. {
  538. if (NULL == pThis->m_hKbdAccel ||
  539. !TranslateAccelerator(pThis->m_hwndMain,
  540. pThis->m_hKbdAccel,
  541. &msg))
  542. {
  543. TranslateMessage(&msg);
  544. DispatchMessage(&msg);
  545. }
  546. }
  547. }
  548. }
  549. }
  550. else
  551. {
  552. DBGERROR((TEXT("LISTVIEW - OleInitialize failed for thread %d."),
  553. GetCurrentThreadId()));
  554. }
  555. OleUninitialize();
  556. }
  557. }
  558. catch(CAllocException& e)
  559. {
  560. DiskQuotaMsgBox(GetDesktopWindow(),
  561. IDS_OUTOFMEMORY,
  562. IDS_TITLE_DISK_QUOTA,
  563. MB_ICONERROR | MB_OK);
  564. pThis->CleanupAfterAbnormalTermination();
  565. }
  566. DBGPRINT((DM_VIEW, DL_HIGH, TEXT("LISTVIEW - Exit thread %d"), GetCurrentThreadId()));
  567. //
  568. // Release the view object since it's no longer required.
  569. // This will call the destructor.
  570. //
  571. pThis->Release();
  572. InterlockedDecrement(&g_cRefThisDll);
  573. return 0;
  574. }
  575. ///////////////////////////////////////////////////////////////////////////////
  576. /* Function: DetailsView::CleanupAfterAbnormalTermination
  577. Description: Perform operations required after the thread has terminated
  578. abnormally. This function assumes that the thread's message pump is
  579. no longer active. Any operations performed must not generate messages
  580. that require processing by the thread.
  581. This method does almost the same things as OnDestroy().
  582. Arguments: None.
  583. Returns: Nothing.
  584. Revision History:
  585. Date Description Programmer
  586. -------- --------------------------------------------------- ----------
  587. 12/15/96 Initial creation. BrianAu
  588. */
  589. ///////////////////////////////////////////////////////////////////////////////
  590. VOID
  591. DetailsView::CleanupAfterAbnormalTermination(
  592. VOID
  593. )
  594. {
  595. //
  596. // Cancel subclassing of the listview control.
  597. //
  598. if (NULL != m_lpfnLVWndProc)
  599. SetWindowLongPtr(m_hwndListView, GWLP_WNDPROC, (INT_PTR)m_lpfnLVWndProc);
  600. DisconnectEventSink();
  601. //
  602. // NOTE: We can't call ReleaseObjects() because that method
  603. // requires an active listview. Our thread is finished and the
  604. // window is gone.
  605. //
  606. if (NULL != m_pQuotaControl)
  607. {
  608. m_pQuotaControl->Release();
  609. m_pQuotaControl = NULL;
  610. }
  611. //
  612. // If we have a data object on the clipboard, clear the clipboard.
  613. // Note that the clipboard holds the reference to the data object.
  614. // When we clear the clipboard, the data object will be released.
  615. //
  616. if (NULL != m_pIDataObjectOnClipboard &&
  617. S_OK == OleIsCurrentClipboard(m_pIDataObjectOnClipboard))
  618. {
  619. OleFlushClipboard();
  620. }
  621. }
  622. ///////////////////////////////////////////////////////////////////////////////
  623. /* Function: DetailsView::OnUserNameChanged
  624. Description: Called by the event source (SidNameResolver) whenever a disk
  625. quota user object's name has changed. The user object's folder and
  626. account name strings are updated in the list view.
  627. Arguments:
  628. pUser - Address of IDiskQuotaUser interface for user object that has
  629. a new name.
  630. Returns:
  631. NO_ERROR - Success.
  632. E_INVALIDARG - User object pointer received from event source was invalid.
  633. E_FAIL - User not found in listview list.
  634. Revision History:
  635. Date Description Programmer
  636. -------- --------------------------------------------------- ----------
  637. 08/20/96 Initial creation. BrianAu
  638. 12/10/96 Use free-threading OLE apartment model. BrianAu
  639. 02/05/98 Changed ListView_RedrawItems to use BrianAu
  640. SendMessageTimeout().
  641. */
  642. ///////////////////////////////////////////////////////////////////////////////
  643. STDMETHODIMP
  644. DetailsView::OnUserNameChanged(
  645. PDISKQUOTA_USER pUser
  646. )
  647. {
  648. HRESULT hResult = E_FAIL;
  649. //
  650. // Ensure the DetailsView object stay's alive while the view is updated.
  651. // Remember, this code is being run on the SID/Name resolver's thread.
  652. //
  653. AddRef();
  654. //
  655. // We don't want to perform a user-name-changed update if the
  656. // view is being or has been destroyed. Likewise, we don't want to
  657. // destroy the view window while a user-name-changed update is in
  658. // progress. The crit sec m_csAsyncUpdate and the flag m_bDestroyingView
  659. // work together to ensure this.
  660. //
  661. EnterCriticalSection(&m_csAsyncUpdate);
  662. if (!m_bDestroyingView)
  663. {
  664. try
  665. {
  666. if (NULL != pUser)
  667. {
  668. INT iItem = -1;
  669. if (m_UserList.FindIndex((LPVOID)pUser, &iItem))
  670. {
  671. //
  672. // Send message to listview to redraw the item
  673. // that changed. Use the "timeout" version of
  674. // SendMessage because the main window thread
  675. // could be blocked waiting for m_csAsyncUpdate
  676. // which is now owned by the resolver thread.
  677. // If the main thread is blocked (waiting to
  678. // process WM_DESTROY), this call will return 0
  679. // after 5 seconds. If this happens, we leave the
  680. // CS without generating a window update, releasing
  681. // the CS and letting the main window thread continue
  682. // with WM_DESTROY processing.
  683. //
  684. DWORD_PTR dwResult;
  685. LRESULT lResult = SendMessageTimeout(m_hwndListView,
  686. LVM_REDRAWITEMS,
  687. (WPARAM)iItem,
  688. (LPARAM)iItem,
  689. SMTO_BLOCK,
  690. 5000,
  691. &dwResult);
  692. if (lResult)
  693. UpdateWindow(m_hwndListView);
  694. else
  695. DBGERROR((TEXT("ListView update timed out after 5 seconds")));
  696. hResult = NO_ERROR;
  697. }
  698. }
  699. else
  700. hResult = E_INVALIDARG;
  701. }
  702. catch(CAllocException& e)
  703. {
  704. //
  705. // Catch allocation exceptions and do nothing.
  706. // Resolver doesn't care about return value.
  707. // Want to ensure that Release() is called no matter what.
  708. //
  709. hResult = E_OUTOFMEMORY;
  710. }
  711. }
  712. LeaveCriticalSection(&m_csAsyncUpdate);
  713. Release();
  714. return hResult;
  715. }
  716. ///////////////////////////////////////////////////////////////////////////////
  717. /* Function: DetailsView::CreateMainWindow
  718. Description: Creates the main window for the details view.
  719. Arguments: None.
  720. Returns:
  721. NO_ERROR - Success.
  722. E_FAIL - Couldn't create window.
  723. Revision History:
  724. Date Description Programmer
  725. -------- --------------------------------------------------- ----------
  726. 08/20/96 Initial creation. BrianAu
  727. */
  728. ///////////////////////////////////////////////////////////////////////////////
  729. HRESULT
  730. DetailsView::CreateMainWindow(
  731. VOID
  732. )
  733. {
  734. HRESULT hResult = NO_ERROR;
  735. WNDCLASSEX wc;
  736. DWORD dwExStyle;
  737. LANGID LangID;
  738. wc.cbSize = sizeof(WNDCLASSEX);
  739. wc.style = CS_PARENTDC;
  740. wc.lpfnWndProc = WndProc;
  741. wc.cbClsExtra = 0;
  742. wc.cbWndExtra = 0;
  743. wc.hInstance = g_hInstDll;
  744. wc.hIcon = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_QUOTA));
  745. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  746. wc.hbrBackground = NULL;
  747. wc.lpszMenuName = MAKEINTRESOURCE(IDM_LISTVIEW_MENU);
  748. wc.lpszClassName = c_szWndClassDetailsView;
  749. wc.hIconSm = NULL;
  750. RegisterClassEx(&wc);
  751. //
  752. // Need to pass "this" pointer in WM_CREATE. We'll store "this"
  753. // in the window's USERDATA.
  754. //
  755. WNDCREATE_DATA wcd;
  756. wcd.cbExtra = sizeof(WNDCREATE_DATA);
  757. wcd.pThis = this;
  758. //
  759. // Create the window title string.
  760. // "Quota Details for My Disk (X:)"
  761. //
  762. CString strWndTitle(g_hInstDll, IDS_TITLE_MAINWINDOW, (LPCTSTR)m_strVolumeDisplayName);
  763. HWND hwndDesktop = GetDesktopWindow();
  764. HDC hdc = GetDC(hwndDesktop);
  765. //
  766. // Get current screen resolution.
  767. //
  768. if ((m_lvsi.cxScreen != (WORD)GetDeviceCaps(hdc, HORZRES)) ||
  769. (m_lvsi.cyScreen != (WORD)GetDeviceCaps(hdc, VERTRES)))
  770. {
  771. //
  772. // Screen resolution has changed since listview state data was
  773. // last saved to registry. Use the default window ht/wd.
  774. //
  775. m_lvsi.cx = 0;
  776. m_lvsi.cy = 0;
  777. }
  778. ReleaseDC(hwndDesktop, hdc);
  779. // Check if we are running on BiDi Localized build. we need to create the Main Window
  780. // mirrored (WS_EX_LAYOUTRTL).
  781. dwExStyle = 0;
  782. LangID = GetUserDefaultUILanguage();
  783. if( LangID )
  784. {
  785. LCID iLCID;
  786. WCHAR wchLCIDFontSignature[16];
  787. iLCID = MAKELCID( LangID , SORT_DEFAULT );
  788. if( GetLocaleInfoW( iLCID ,
  789. LOCALE_FONTSIGNATURE ,
  790. (WCHAR *) &wchLCIDFontSignature[0] ,
  791. (sizeof(wchLCIDFontSignature)/sizeof(WCHAR))) )
  792. {
  793. if( wchLCIDFontSignature[7] & (WCHAR)0x0800 )
  794. {
  795. dwExStyle = WS_EX_LAYOUTRTL;
  796. }
  797. }
  798. }
  799. m_hwndMain = CreateWindowEx(dwExStyle,
  800. c_szWndClassDetailsView,
  801. strWndTitle,
  802. WS_OVERLAPPEDWINDOW,
  803. CW_USEDEFAULT,
  804. CW_USEDEFAULT,
  805. m_lvsi.cx ? m_lvsi.cx : CW_USEDEFAULT,
  806. m_lvsi.cy ? m_lvsi.cy : CW_USEDEFAULT,
  807. hwndDesktop,
  808. NULL,
  809. g_hInstDll,
  810. &wcd);
  811. if (NULL != m_hwndMain)
  812. {
  813. //
  814. // Register the main window as an OLE drop target.
  815. //
  816. RegisterAsDropTarget(TRUE);
  817. }
  818. else
  819. {
  820. hResult = E_FAIL;
  821. }
  822. #if DBG
  823. if (FAILED(hResult))
  824. DBGERROR((TEXT("LISTVIEW - Failed creating main window."), hResult));
  825. #endif
  826. return hResult;
  827. }
  828. ///////////////////////////////////////////////////////////////////////////////
  829. /* Function: DetailsView::CreateListView
  830. Description: Create the list view control.
  831. Arguments: None.
  832. Returns:
  833. NO_ERROR - Success.
  834. E_FAIL - Failed to create listview or load icons.
  835. Revision History:
  836. Date Description Programmer
  837. -------- --------------------------------------------------- ----------
  838. 08/20/96 Initial creation. BrianAu
  839. 02/21/97 Modified to use virtual listview (LVS_OWNERDATA) BrianAu
  840. */
  841. ///////////////////////////////////////////////////////////////////////////////
  842. HRESULT
  843. DetailsView::CreateListView(
  844. VOID
  845. )
  846. {
  847. HRESULT hResult = NO_ERROR;
  848. RECT rc;
  849. DBGASSERT((NULL != m_hwndMain));
  850. GetClientRect(m_hwndMain, &rc);
  851. m_hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE,
  852. WC_LISTVIEW,
  853. TEXT(""),
  854. WS_CHILD | WS_CLIPCHILDREN |
  855. WS_VISIBLE | WS_CLIPSIBLINGS |
  856. WS_TABSTOP | LVS_REPORT | LVS_SHOWSELALWAYS |
  857. LVS_OWNERDATA,
  858. 0, 0,
  859. rc.right - rc.left,
  860. rc.bottom - rc.top,
  861. m_hwndMain,
  862. (HMENU)NULL,
  863. g_hInstDll,
  864. NULL);
  865. if (NULL != m_hwndListView)
  866. {
  867. //
  868. // Store "this" ptr so subclass WndProc can access members.
  869. //
  870. SetWindowLongPtr(m_hwndListView, GWLP_USERDATA, (INT_PTR)this);
  871. //
  872. // We talk to the header control so save it's handle.
  873. //
  874. m_hwndHeader = ListView_GetHeader(m_hwndListView);
  875. //
  876. // Subclass the listview control so we can monitor mouse position.
  877. // This is used for listview tooltip management.
  878. //
  879. m_lpfnLVWndProc = (WNDPROC)GetWindowLongPtr(m_hwndListView, GWLP_WNDPROC);
  880. SetWindowLongPtr(m_hwndListView, GWLP_WNDPROC, (INT_PTR)LVSubClassWndProc);
  881. //
  882. // Enable listview for images in sub-item columns and full-row select.
  883. //
  884. ListView_SetExtendedListViewStyle(m_hwndListView,
  885. LVS_EX_SUBITEMIMAGES |
  886. LVS_EX_FULLROWSELECT |
  887. LVS_EX_HEADERDRAGDROP);
  888. //
  889. // Add all columns to the listview.
  890. // Adjust for showing/hiding the Folder column.
  891. //
  892. INT iColId = 0; // Start with 1st col.
  893. for (INT iSubItem = 0;
  894. iSubItem < (m_lvsi.fShowFolder ? DetailsView::idCol_Last : DetailsView::idCol_Last - 1);
  895. iSubItem++)
  896. {
  897. AddColumn(iSubItem, g_rgColumns[iColId]);
  898. iColId++;
  899. //
  900. // Skip over the Folder column if it's hidden.
  901. //
  902. if (!m_lvsi.fShowFolder && DetailsView::idCol_Folder == iColId)
  903. iColId++;
  904. }
  905. //
  906. // Restore column widths to where the user left them last time
  907. // the details view was used.
  908. //
  909. if (m_lvsi.cb == sizeof(LV_STATE_INFO))
  910. {
  911. for (UINT i = 0; i < DetailsView::idCol_Last; i++)
  912. {
  913. if (0 != m_lvsi.rgcxCol[i])
  914. {
  915. ListView_SetColumnWidth(m_hwndListView, i, m_lvsi.rgcxCol[i]);
  916. }
  917. }
  918. }
  919. //
  920. // Restore the user's last column ordering.
  921. //
  922. DBGASSERT((Header_GetItemCount(m_hwndHeader) <= ARRAYSIZE(m_lvsi.rgColIndices)));
  923. Header_SetOrderArray(m_hwndHeader, Header_GetItemCount(m_hwndHeader),
  924. m_lvsi.rgColIndices);
  925. //
  926. // Check the "Show Folder" menu item to indicate the current visibility state
  927. // of the Folder column.
  928. //
  929. CheckMenuItem(GetMenu(m_hwndMain),
  930. IDM_VIEW_SHOWFOLDER,
  931. MF_BYCOMMAND | (m_lvsi.fShowFolder ? MF_CHECKED : MF_UNCHECKED));
  932. //
  933. // Set the sensitivity of the "by Folder" item arrangement menu option.
  934. //
  935. EnableMenuItem_ArrangeByFolder(m_lvsi.fShowFolder);
  936. //
  937. // Create and activate the listview tooltip window.
  938. // Even though the standard listview has a tooltip window, we need more
  939. // control that it provides. i.e.: We need to be able to enable/disable
  940. // the tooltip as well as notify the control when a new listview item
  941. // has been hit. Therefore, we need our own tooltip window.
  942. //
  943. if (SUCCEEDED(CreateListViewToolTip()))
  944. ActivateListViewToolTip(!m_lvsi.fShowFolder);
  945. else
  946. DBGERROR((TEXT("LISTVIEW, Failed creating tooltip window.")));
  947. //
  948. // Add WARNING and ERROR images to the listview's image list.
  949. // These are used for the "Status" column.
  950. //
  951. if (FAILED(hResult = AddImages()))
  952. DBGERROR((TEXT("LISTVIEW, Failed adding images to image list.")));
  953. }
  954. else
  955. hResult = E_FAIL;
  956. #if DBG
  957. if (FAILED(hResult))
  958. DBGERROR((TEXT("LISTVIEW - Failed creating list view with result 0x%08X"),
  959. hResult));
  960. #endif
  961. return hResult;
  962. }
  963. ///////////////////////////////////////////////////////////////////////////////
  964. /* Function: DetailsView::RemoveColumn
  965. Description: Removes a specified column from the list view.
  966. Arguments:
  967. iColId - 0-based index of the column in the list view.
  968. i.e. idCol_Folder, idCol_Name etc.
  969. Returns:
  970. NO_ERROR - Success.
  971. E_FAIL - Column could not be removed.
  972. Revision History:
  973. Date Description Programmer
  974. -------- --------------------------------------------------- ----------
  975. 09/06/96 Initial creation. BrianAu
  976. */
  977. ///////////////////////////////////////////////////////////////////////////////
  978. HRESULT
  979. DetailsView::RemoveColumn(
  980. INT iColId
  981. )
  982. {
  983. INT iSubItem = m_ColMap.IdToSubItem(iColId);
  984. if (-1 != iSubItem && ListView_DeleteColumn(m_hwndListView, iSubItem))
  985. {
  986. m_ColMap.RemoveId(iSubItem);
  987. return NO_ERROR;
  988. }
  989. return E_FAIL;
  990. }
  991. ///////////////////////////////////////////////////////////////////////////////
  992. /* Function: DetailsView::AddColumn
  993. Description: Adds a column to the list view. The caller specifies which
  994. 0-based position the column is to occupy and a reference to a column
  995. descriptor record containing information that defines the column.
  996. Arguments:
  997. iSubItem - 0-based index of the column in the list view.
  998. ColDesc - Reference to column descriptor record.
  999. Returns:
  1000. NO_ERROR - Success.
  1001. E_FAIL - One or more columns could not be inserted.
  1002. Revision History:
  1003. Date Description Programmer
  1004. -------- --------------------------------------------------- ----------
  1005. 09/06/96 Initial creation. BrianAu
  1006. */
  1007. ///////////////////////////////////////////////////////////////////////////////
  1008. HRESULT
  1009. DetailsView::AddColumn(
  1010. INT iSubItem,
  1011. const DV_COLDATA& ColDesc
  1012. )
  1013. {
  1014. LV_COLUMN col;
  1015. HRESULT hResult = NO_ERROR;
  1016. CString strColText(g_hInstDll, ColDesc.idMsgText);
  1017. col.pszText = strColText;
  1018. if (0 == ColDesc.cx)
  1019. {
  1020. //
  1021. // No width specified in col desc record. Size column to the title.
  1022. //
  1023. HDC hdc = NULL;
  1024. TEXTMETRIC tm;
  1025. hdc = GetDC(m_hwndListView);
  1026. GetTextMetrics(hdc, &tm);
  1027. ReleaseDC(m_hwndListView, hdc);
  1028. //
  1029. // Nothing special about the +2. Without it, we get trailing ellipsis.
  1030. //
  1031. col.cx = tm.tmAveCharWidth * (lstrlen(col.pszText) + 2);
  1032. }
  1033. else
  1034. col.cx = ColDesc.cx; // Use width from col descriptor.
  1035. col.iSubItem = iSubItem;
  1036. col.fmt = ColDesc.fmt;
  1037. col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
  1038. if (-1 != ListView_InsertColumn(m_hwndListView, iSubItem, &col))
  1039. m_ColMap.InsertId(iSubItem, ColDesc.iColId);
  1040. else
  1041. hResult = E_FAIL;
  1042. return hResult;
  1043. }
  1044. ///////////////////////////////////////////////////////////////////////////////
  1045. /* Function: DetailsView::AddImages
  1046. Description: Adds icon images to the list view's image lists. These
  1047. icons are used in the status column to indicate overrun of the
  1048. quota threshold and limit.
  1049. Arguments: None.
  1050. Returns:
  1051. NO_ERROR - Success.
  1052. E_FAIL - One or more icons could not be loaded.
  1053. Revision History:
  1054. Date Description Programmer
  1055. -------- --------------------------------------------------- ----------
  1056. 09/06/96 Initial creation. BrianAu
  1057. */
  1058. ///////////////////////////////////////////////////////////////////////////////
  1059. HRESULT
  1060. DetailsView::AddImages(
  1061. VOID
  1062. )
  1063. {
  1064. HRESULT hResult = NO_ERROR;
  1065. HIMAGELIST hSmallImages = NULL;
  1066. //
  1067. // Create the image lists for the listview.
  1068. //
  1069. hSmallImages = ImageList_Create(BITMAP_WIDTH, BITMAP_HEIGHT, ILC_MASK, 3, 0);
  1070. //
  1071. // Note: The order of these icon ID's in this array must match with the
  1072. // iIMAGELIST_ICON_XXXXX macros defined at the top of this file.
  1073. // The macro values represent the image indices in the image list.
  1074. //
  1075. struct IconDef
  1076. {
  1077. LPTSTR szName;
  1078. HINSTANCE hInstDll;
  1079. } rgIcons[] = {
  1080. { MAKEINTRESOURCE(IDI_OKBUBBLE), g_hInstDll },
  1081. { IDI_WARNING, NULL },
  1082. { MAKEINTRESOURCE(IDI_WARNERR), g_hInstDll }
  1083. };
  1084. for (UINT i = 0; i < ARRAYSIZE(rgIcons) && SUCCEEDED(hResult); i++)
  1085. {
  1086. HICON hIcon = LoadIcon(rgIcons[i].hInstDll, rgIcons[i].szName);
  1087. if (NULL != hIcon)
  1088. {
  1089. ImageList_AddIcon(hSmallImages, hIcon);
  1090. DestroyIcon(hIcon);
  1091. }
  1092. else
  1093. {
  1094. DBGERROR((TEXT("LISTVIEW - Error loading icon")));
  1095. hResult = E_FAIL;
  1096. }
  1097. }
  1098. ImageList_SetBkColor(hSmallImages, CLR_NONE); // Transparent background.
  1099. ListView_SetImageList(m_hwndListView, hSmallImages, LVSIL_SMALL);
  1100. return hResult;
  1101. }
  1102. ///////////////////////////////////////////////////////////////////////////////
  1103. /* Function: DetailsView::CreateListViewToolTip
  1104. Description: Creates a tooltip window for displaying the user's folder
  1105. name when the Folder column is hidden. The entire listview
  1106. is defined as a single tool. We make the tooltip control think
  1107. each listview item is a separate tool by intercepting WM_MOUSEMOVE,
  1108. and performing a hit test to determine which listview item is hit.
  1109. If the cursor has moved over a new item, the tooltip control is sent
  1110. a WM_MOUSEMOVE(0,0). The next real WM_MOUSEMOVE that we relay to the
  1111. tooltip makes it think that it is on a new tool. This is required
  1112. so that tooltips popup and hide appropriately.
  1113. Arguments: None.
  1114. Returns:
  1115. NO_ERROR - Success.
  1116. E_FAIL - Failed to create tooltip window.
  1117. Revision History:
  1118. Date Description Programmer
  1119. -------- --------------------------------------------------- ----------
  1120. 09/09/96 Initial creation. BrianAu
  1121. */
  1122. ///////////////////////////////////////////////////////////////////////////////
  1123. HRESULT
  1124. DetailsView::CreateListViewToolTip(
  1125. VOID
  1126. )
  1127. {
  1128. HRESULT hResult = E_FAIL;
  1129. m_hwndListViewToolTip = CreateWindowEx(0,
  1130. TOOLTIPS_CLASS,
  1131. (LPTSTR)NULL,
  1132. TTS_ALWAYSTIP,
  1133. CW_USEDEFAULT,
  1134. CW_USEDEFAULT,
  1135. CW_USEDEFAULT,
  1136. CW_USEDEFAULT,
  1137. m_hwndListView,
  1138. (HMENU)NULL,
  1139. g_hInstDll,
  1140. NULL);
  1141. if (NULL != m_hwndListViewToolTip)
  1142. {
  1143. TOOLINFO ti;
  1144. //
  1145. // Set tooltip timing parameter so that it pops up after
  1146. // 1/2 second of no-mouse-movement.
  1147. //
  1148. SendMessage(m_hwndListViewToolTip,
  1149. TTM_SETDELAYTIME,
  1150. TTDT_INITIAL,
  1151. (LPARAM)500);
  1152. ti.cbSize = sizeof(TOOLINFO);
  1153. ti.uFlags = TTF_IDISHWND;
  1154. ti.hwnd = m_hwndListView;
  1155. ti.hinst = g_hInstDll;
  1156. ti.uId = (UINT_PTR)m_hwndListView; // Treat entire LV as a tool.
  1157. ti.lpszText = LPSTR_TEXTCALLBACK;
  1158. if (SendMessage(m_hwndListViewToolTip,
  1159. TTM_ADDTOOL,
  1160. 0,
  1161. (LPARAM)&ti))
  1162. {
  1163. hResult = NO_ERROR;
  1164. }
  1165. }
  1166. return hResult;
  1167. }
  1168. ///////////////////////////////////////////////////////////////////////////////
  1169. /* Function: DetailsView::CreateStatusBar
  1170. Description: Creates the status bar.
  1171. Arguments: None.
  1172. Returns:
  1173. NO_ERROR - Success.
  1174. E_FAIL - Failed to create status bar.
  1175. Revision History:
  1176. Date Description Programmer
  1177. -------- --------------------------------------------------- ----------
  1178. 08/20/96 Initial creation. BrianAu
  1179. */
  1180. ///////////////////////////////////////////////////////////////////////////////
  1181. HRESULT
  1182. DetailsView::CreateStatusBar(
  1183. VOID
  1184. )
  1185. {
  1186. HRESULT hResult = NO_ERROR;
  1187. DBGASSERT((NULL != m_hwndMain));
  1188. m_hwndStatusBar = CreateWindow(STATUSCLASSNAME,
  1189. TEXT(""),
  1190. WS_VISIBLE | WS_CHILD | WS_BORDER | SBS_SIZEGRIP,
  1191. 0, 0, 0, 0,
  1192. m_hwndMain,
  1193. (HMENU)NULL,
  1194. g_hInstDll,
  1195. NULL);
  1196. if (NULL != m_hwndStatusBar)
  1197. {
  1198. //
  1199. // Show/hide status bar according to registry setting.
  1200. //
  1201. if (!m_lvsi.fStatusBar)
  1202. ShowWindow(m_hwndStatusBar, SW_HIDE);
  1203. //
  1204. // Check the menu item to indicate the current status bar state.
  1205. //
  1206. CheckMenuItem(GetMenu(m_hwndMain),
  1207. IDM_VIEW_STATUSBAR,
  1208. MF_BYCOMMAND | (m_lvsi.fStatusBar ? MF_CHECKED : MF_UNCHECKED));
  1209. }
  1210. else
  1211. {
  1212. hResult = E_FAIL;
  1213. DBGERROR((TEXT("LISTVIEW - Failed creating status bar."), hResult));
  1214. }
  1215. return hResult;
  1216. }
  1217. ///////////////////////////////////////////////////////////////////////////////
  1218. /* Function: DetailsView::CreateToolBar
  1219. Description: Creates the tool bar.
  1220. Arguments: None.
  1221. Returns:
  1222. NO_ERROR - Success.
  1223. E_FAIL - Failed to create status bar.
  1224. Revision History:
  1225. Date Description Programmer
  1226. -------- --------------------------------------------------- ----------
  1227. 08/20/96 Initial creation. BrianAu
  1228. 02/26/97 Changed to flat toolbar buttons. BrianAu
  1229. 05/20/97 Added "Find User" button and combo box. BrianAu
  1230. */
  1231. ///////////////////////////////////////////////////////////////////////////////
  1232. HRESULT
  1233. DetailsView::CreateToolBar(
  1234. VOID
  1235. )
  1236. {
  1237. HRESULT hResult = NO_ERROR;
  1238. //
  1239. // Array describing each of the tool bar buttons.
  1240. //
  1241. TBBUTTON rgToolBarBtns[] = {
  1242. { STD_FILENEW, IDM_QUOTA_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
  1243. { STD_DELETE, IDM_QUOTA_DELETE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
  1244. { STD_PROPERTIES, IDM_QUOTA_PROPERTIES, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
  1245. { STD_UNDO, IDM_EDIT_UNDO, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0},
  1246. { STD_FIND, IDM_EDIT_FIND, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}
  1247. };
  1248. DBGASSERT((NULL != m_hwndMain));
  1249. m_hwndToolBar = CreateToolbarEx(m_hwndMain,
  1250. WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
  1251. IDC_TOOLBAR,
  1252. 15,
  1253. (HINSTANCE)HINST_COMMCTRL,
  1254. IDB_STD_SMALL_COLOR,
  1255. (LPCTBBUTTON)rgToolBarBtns,
  1256. ARRAYSIZE(rgToolBarBtns),
  1257. 0,
  1258. 0,
  1259. 100,
  1260. 30,
  1261. sizeof(TBBUTTON));
  1262. if (NULL != m_hwndToolBar)
  1263. {
  1264. //
  1265. // FEATURE: I'm creating this combo without the WS_VISIBLE
  1266. // attribute set. I originally coded this to have a
  1267. // "find" dropdown combo in the toolbar similar to that
  1268. // found in MS Dev Studio. Later we decided that this
  1269. // was unnecessarily redundant with the "find" dialog
  1270. // and it's dropdown combo. I'm leaving the code for
  1271. // two reasons.
  1272. // 1. I don't want to break the existing implementation.
  1273. // 2. If we decide later to re-enable the feature it will
  1274. // be easy to reactivate.
  1275. //
  1276. // [brianau - 1/20/98]
  1277. //
  1278. m_hwndToolbarCombo = CreateWindowEx(0,
  1279. TEXT("COMBOBOX"),
  1280. TEXT(""),
  1281. WS_CHILD | WS_BORDER |
  1282. CBS_HASSTRINGS | CBS_DROPDOWN |
  1283. CBS_AUTOHSCROLL,
  1284. 0, 0,
  1285. CX_TOOLBAR_COMBO,
  1286. CY_TOOLBAR_COMBO,
  1287. m_hwndToolBar,
  1288. (HMENU)IDC_TOOLBAR_COMBO,
  1289. g_hInstDll,
  1290. NULL);
  1291. if (NULL != m_hwndToolbarCombo)
  1292. {
  1293. //
  1294. // Set the font in the toolbar combo to be the same as that
  1295. // used in listview. This assumes that the listview
  1296. // has already been created.
  1297. //
  1298. DBGASSERT((NULL != m_hwndListView));
  1299. HFONT hfontMain = (HFONT)SendMessage(m_hwndListView, WM_GETFONT, 0, 0);
  1300. SendMessage(m_hwndToolbarCombo, WM_SETFONT, (WPARAM)hfontMain, 0);
  1301. //
  1302. // Initialize the "user finder" object so that it knows
  1303. // how to communicate with the toolbar combo box.
  1304. //
  1305. m_pUserFinder->ConnectToolbarCombo(m_hwndToolbarCombo);
  1306. //
  1307. // Retrieve the finder's MRU list contents from the registry and
  1308. // load the toolbar's combo box.
  1309. // The check for cMruEntries < MAX_FINDMRU_ENTRIES is to prevent
  1310. // this loop from running wild if someone trashes the registry entry.
  1311. //
  1312. RegKey keyPref(HKEY_CURRENT_USER, REGSTR_KEY_DISKQUOTA);
  1313. if (SUCCEEDED(keyPref.Open(KEY_READ)))
  1314. {
  1315. CArray<CString> rgstrMRU;
  1316. if (SUCCEEDED(keyPref.GetValue(REGSTR_VAL_FINDMRU, &rgstrMRU)))
  1317. {
  1318. int n = rgstrMRU.Count();
  1319. for (int i = 0; i < n; i++)
  1320. {
  1321. SendMessage(m_hwndToolbarCombo,
  1322. CB_ADDSTRING,
  1323. 0,
  1324. (LPARAM)(rgstrMRU[i].Cstr()));
  1325. }
  1326. }
  1327. }
  1328. }
  1329. //
  1330. // Show/hide tool bar according to registry setting.
  1331. //
  1332. if (!m_lvsi.fToolBar)
  1333. ShowWindow(m_hwndToolBar, SW_HIDE);
  1334. //
  1335. // Check the menu item to indicate the current tool bar state.
  1336. //
  1337. CheckMenuItem(GetMenu(m_hwndMain),
  1338. IDM_VIEW_TOOLBAR,
  1339. MF_BYCOMMAND | (m_lvsi.fToolBar ? MF_CHECKED : MF_UNCHECKED));
  1340. //
  1341. // Initially, we have nothing in the undo list.
  1342. //
  1343. EnableMenuItem_Undo(FALSE);
  1344. }
  1345. else
  1346. {
  1347. hResult = E_FAIL;
  1348. DBGERROR((TEXT("LISTVIEW - Failed creating tool bar."), hResult));
  1349. }
  1350. return hResult;
  1351. }
  1352. ///////////////////////////////////////////////////////////////////////////////
  1353. /* Function: DetailsView::LoadObjects
  1354. Description: Loads the user object list with quota record objects.
  1355. Arguments: None.
  1356. Returns:
  1357. NO_ERROR - Success.
  1358. E_FAIL - Failed enumerating users or adding objects to listview.
  1359. Revision History:
  1360. Date Description Programmer
  1361. -------- --------------------------------------------------- ----------
  1362. 08/20/96 Initial creation. BrianAu
  1363. */
  1364. ///////////////////////////////////////////////////////////////////////////////
  1365. HRESULT
  1366. DetailsView::LoadObjects(
  1367. VOID
  1368. )
  1369. {
  1370. HRESULT hResult = NO_ERROR;
  1371. DBGASSERT((NULL != m_pQuotaControl));
  1372. //
  1373. // Use a user enumerator object for obtaining all of the quota users.
  1374. //
  1375. IEnumDiskQuotaUsers *pEnumUsers = NULL;
  1376. hResult = m_pQuotaControl->CreateEnumUsers(
  1377. NULL, // All entries.
  1378. 0, // All entries.
  1379. DISKQUOTA_USERNAME_RESOLVE_ASYNC, // Asynchronous operation.
  1380. &pEnumUsers);
  1381. if (SUCCEEDED(hResult))
  1382. {
  1383. IDiskQuotaUser *pUser = NULL;
  1384. hResult = S_OK;
  1385. //
  1386. // m_bStopLoadingObjects is sort of a hack so that we can interrupt
  1387. // object loading if the user closes the view while loading is in progress.
  1388. //
  1389. // This is probably the most speed-critical loop in the disk quota UI.
  1390. // The faster it is, the less time the user must wait for the listview
  1391. // to be populated with user objects.
  1392. //
  1393. try
  1394. {
  1395. //
  1396. // Go ahead and take a lock on the user list during the entire loading
  1397. // process. This will let the list locking code in m_UserList.Append
  1398. // proceded without having to obtain the lock each time.
  1399. //
  1400. m_UserList.Lock();
  1401. while(!m_bStopLoadingObjects)
  1402. {
  1403. DWORD cUsers = 1;
  1404. hResult = pEnumUsers->Next(cUsers, &pUser, &cUsers);
  1405. if (S_OK == hResult)
  1406. {
  1407. m_UserList.Append(pUser);
  1408. }
  1409. else
  1410. {
  1411. break;
  1412. }
  1413. pUser = NULL;
  1414. }
  1415. pEnumUsers->Release(); // Release the enumerator.
  1416. pEnumUsers = NULL;
  1417. m_UserList.ReleaseLock();
  1418. }
  1419. catch(CAllocException& e)
  1420. {
  1421. //
  1422. // Clean up before re-throwing exception.
  1423. // Leave m_UserList in the pre-exception state.
  1424. //
  1425. if (NULL != pUser)
  1426. pUser->Release();
  1427. if (NULL != pEnumUsers)
  1428. pEnumUsers->Release();
  1429. m_UserList.ReleaseLock();
  1430. m_bStopLoadingObjects = FALSE;
  1431. hResult = E_OUTOFMEMORY;
  1432. }
  1433. }
  1434. if (S_FALSE == hResult) // Means no-more-users.
  1435. hResult = NO_ERROR;
  1436. #if DBG
  1437. if (FAILED(hResult))
  1438. {
  1439. DBGERROR((TEXT("LISTVIEW - Failed loading objects. Result = 0x%08X"),
  1440. hResult));
  1441. }
  1442. #endif
  1443. m_bStopLoadingObjects = FALSE;
  1444. return hResult;
  1445. }
  1446. ///////////////////////////////////////////////////////////////////////////////
  1447. /* Function: DetailsView::ReleaseObjects
  1448. Description: Releases all objects from the user object list (listview).
  1449. Arguments: None.
  1450. Returns: Always returns NO_ERROR.
  1451. Revision History:
  1452. Date Description Programmer
  1453. -------- --------------------------------------------------- ----------
  1454. 08/20/96 Initial creation. BrianAu
  1455. 02/21/97 Ownerdata listview. Added m_UserList. BrianAu
  1456. */
  1457. ///////////////////////////////////////////////////////////////////////////////
  1458. HRESULT
  1459. DetailsView::ReleaseObjects(
  1460. VOID
  1461. )
  1462. {
  1463. //
  1464. // Destroy the user objects in the list.
  1465. //
  1466. PDISKQUOTA_USER pUser = NULL;
  1467. m_UserList.Lock();
  1468. while(m_UserList.RemoveLast((LPVOID *)&pUser))
  1469. {
  1470. if (NULL != pUser)
  1471. pUser->Release();
  1472. }
  1473. m_UserList.ReleaseLock();
  1474. return NO_ERROR;
  1475. }
  1476. ///////////////////////////////////////////////////////////////////////////////
  1477. /* Function: DetailsView::SortObjects
  1478. Description: Sort objects in the list view using a given column as the key.
  1479. Arguments:
  1480. idColumn - Number of the column (0-based) to use as the key.
  1481. dwDirection - 0 = Ascending sort, 1 = Descending sort.
  1482. Returns:
  1483. Revision History:
  1484. Date Description Programmer
  1485. -------- --------------------------------------------------- ----------
  1486. 08/20/96 Initial creation. BrianAu
  1487. 02/24/97 Added m_UserList. Ownerdata listview. BrianAu
  1488. */
  1489. ///////////////////////////////////////////////////////////////////////////////
  1490. LRESULT
  1491. DetailsView::SortObjects(
  1492. DWORD idColumn,
  1493. DWORD dwDirection
  1494. )
  1495. {
  1496. DBGASSERT((idColumn < DetailsView::idCol_Last));
  1497. CAutoWaitCursor waitcursor;
  1498. COMPARESTRUCT cs;
  1499. cs.idColumn = idColumn;
  1500. cs.dwDirection = dwDirection;
  1501. cs.pThis = this;
  1502. m_UserList.Lock();
  1503. m_UserList.Sort(CompareItems, (LPARAM)&cs);
  1504. InvalidateRect(m_hwndListView, NULL, TRUE);
  1505. UpdateWindow(m_hwndListView);
  1506. m_UserList.ReleaseLock();
  1507. return 0;
  1508. }
  1509. ///////////////////////////////////////////////////////////////////////////////
  1510. /* Function: DetailsView::CompareItems [static]
  1511. Description: Compares two items from the details view.
  1512. Note that it's a static method so there's no "this" pointer.
  1513. Arguments:
  1514. lParam1 - Address of first user object.
  1515. lParam2 - Address of second user object.
  1516. lParam3 - Address of a COMPARESTRUCT structure.
  1517. Returns:
  1518. < 0 = User 1 is "less than" user 2.
  1519. 0 = Users are "equivalent".
  1520. > 0 = User 1 is "greater than" user 2.
  1521. Revision History:
  1522. Date Description Programmer
  1523. -------- --------------------------------------------------- ----------
  1524. 08/20/96 Initial creation. BrianAu
  1525. 09/05/96 Added domain name string. BrianAu
  1526. 05/19/97 Fixed overflow in difference calculations. BrianAu
  1527. Changed type of "diff" from INT to __int64.
  1528. 07/18/97 Use CompareString for name comparisons. BrianAu
  1529. Need to be locale-sensitive.
  1530. */
  1531. ///////////////////////////////////////////////////////////////////////////////
  1532. INT
  1533. DetailsView::CompareItems(
  1534. LPVOID lParam1,
  1535. LPVOID lParam2,
  1536. LPARAM lParam3
  1537. )
  1538. {
  1539. INT i[2];
  1540. __int64 diff = 0;
  1541. PDISKQUOTA_USER pUser[2];
  1542. LONGLONG llValue[2];
  1543. PCOMPARESTRUCT pcs = (PCOMPARESTRUCT)lParam3;
  1544. DBGASSERT((NULL != pcs));
  1545. DetailsView *pThis = pcs->pThis;
  1546. pUser[0] = (PDISKQUOTA_USER)lParam1;
  1547. pUser[1] = (PDISKQUOTA_USER)lParam2;
  1548. i[0] = pcs->dwDirection; // Sort direction (0 = ascending, 1 = descending)
  1549. i[1] = i[0] ^ 1; // Opposite of i[0].
  1550. DBGASSERT((NULL != pUser[0]));
  1551. DBGASSERT((NULL != pUser[1]));
  1552. switch(pcs->idColumn)
  1553. {
  1554. case DetailsView::idCol_Name:
  1555. case DetailsView::idCol_LogonName:
  1556. case DetailsView::idCol_Folder:
  1557. {
  1558. DWORD dwAccountStatus[2];
  1559. pUser[0]->GetAccountStatus(&dwAccountStatus[0]);
  1560. pUser[1]->GetAccountStatus(&dwAccountStatus[1]);
  1561. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus[0] &&
  1562. DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus[1])
  1563. {
  1564. //
  1565. // Both users have valid logon name strings.
  1566. //
  1567. INT iCompareResult; // For CompareString.
  1568. TCHAR szContainer[2][MAX_DOMAIN];
  1569. TCHAR szName[2][MAX_USERNAME];
  1570. TCHAR szLogonName[2][MAX_USERNAME];
  1571. pUser[0]->GetName(szContainer[0], ARRAYSIZE(szContainer[0]),
  1572. szLogonName[0], ARRAYSIZE(szLogonName[0]),
  1573. szName[0], ARRAYSIZE(szName[0]));
  1574. pUser[1]->GetName(szContainer[1], ARRAYSIZE(szContainer[1]),
  1575. szLogonName[1], ARRAYSIZE(szLogonName[1]),
  1576. szName[1], ARRAYSIZE(szName[1]));
  1577. if (DetailsView::idCol_Folder == pcs->idColumn)
  1578. {
  1579. //
  1580. // Sort by container + logon name.
  1581. // Use CompareString so we're locale-sensitive.
  1582. //
  1583. iCompareResult = CompareString(LOCALE_USER_DEFAULT,
  1584. NORM_IGNORECASE,
  1585. szContainer[ i[0] ], -1,
  1586. szContainer[ i[1] ], -1);
  1587. if (CSTR_EQUAL == iCompareResult)
  1588. {
  1589. iCompareResult = CompareString(LOCALE_USER_DEFAULT,
  1590. NORM_IGNORECASE,
  1591. szLogonName[ i[0] ], -1,
  1592. szLogonName[ i[1] ], -1);
  1593. }
  1594. }
  1595. else if (DetailsView::idCol_Name == pcs->idColumn)
  1596. {
  1597. //
  1598. // Sort by display name + container.
  1599. // Use CompareString so we're locale-sensitive.
  1600. //
  1601. iCompareResult = CompareString(LOCALE_USER_DEFAULT,
  1602. NORM_IGNORECASE,
  1603. szName[ i[0] ], -1,
  1604. szName[ i[1] ], -1);
  1605. if (CSTR_EQUAL == iCompareResult)
  1606. {
  1607. iCompareResult = CompareString(LOCALE_USER_DEFAULT,
  1608. NORM_IGNORECASE,
  1609. szContainer[ i[0] ], -1,
  1610. szContainer[ i[1] ], -1);
  1611. }
  1612. }
  1613. else if (DetailsView::idCol_LogonName == pcs->idColumn)
  1614. {
  1615. //
  1616. // Sort by logon name + container.
  1617. // Use CompareString so we're locale-sensitive.
  1618. //
  1619. iCompareResult = CompareString(LOCALE_USER_DEFAULT,
  1620. NORM_IGNORECASE,
  1621. szLogonName[ i[0] ], -1,
  1622. szLogonName[ i[1] ], -1);
  1623. if (CSTR_EQUAL == iCompareResult)
  1624. {
  1625. iCompareResult = CompareString(LOCALE_USER_DEFAULT,
  1626. NORM_IGNORECASE,
  1627. szContainer[ i[0] ], -1,
  1628. szContainer[ i[1] ], -1);
  1629. }
  1630. }
  1631. //
  1632. // Convert iCompareResult [1,2,3] to [-1,0,1].
  1633. //
  1634. diff = iCompareResult - 2;
  1635. }
  1636. else
  1637. {
  1638. //
  1639. // At least one of the users hasn't been or can't be resolved.
  1640. // Compare by account status alone. Status values are such
  1641. // that a resolved name will sort before an unresolved name.
  1642. // Cast to (INT) is required for proper ordering.
  1643. //
  1644. diff = (INT)dwAccountStatus[ i[0] ] - (INT)dwAccountStatus[ i[1] ];
  1645. }
  1646. break;
  1647. }
  1648. case DetailsView::idCol_Status:
  1649. {
  1650. //
  1651. // The status image is based on the quota "state" of the user.
  1652. // This expression effectively compares user records by status image.
  1653. //
  1654. diff = (pThis->GetUserQuotaState(pUser[ i[0] ]) - pThis->GetUserQuotaState(pUser[ i[1] ]));
  1655. break;
  1656. }
  1657. case DetailsView::idCol_AmtUsed:
  1658. {
  1659. pUser[0]->GetQuotaUsed(&llValue[0]);
  1660. pUser[1]->GetQuotaUsed(&llValue[1]);
  1661. diff = llValue[ i[0] ] - llValue[ i[1] ];
  1662. break;
  1663. }
  1664. case DetailsView::idCol_Limit:
  1665. {
  1666. pUser[0]->GetQuotaLimit(&llValue[0]);
  1667. pUser[1]->GetQuotaLimit(&llValue[1]);
  1668. if (NOLIMIT == llValue[ i[0] ])
  1669. diff = 1;
  1670. else if (NOLIMIT == llValue[ i[1] ])
  1671. diff = -1;
  1672. else
  1673. diff = llValue[ i[0] ] - llValue[ i[1] ];
  1674. break;
  1675. }
  1676. case DetailsView::idCol_Threshold:
  1677. {
  1678. pUser[0]->GetQuotaThreshold(&llValue[0]);
  1679. pUser[1]->GetQuotaThreshold(&llValue[1]);
  1680. if (NOLIMIT == llValue[ i[0] ])
  1681. diff = 1;
  1682. else if (NOLIMIT == llValue[ i[1] ])
  1683. diff = -1;
  1684. else
  1685. diff = llValue[ i[0] ] - llValue[ i[1] ];
  1686. break;
  1687. }
  1688. case DetailsView::idCol_PctUsed:
  1689. {
  1690. DWORD dwPct[2];
  1691. CalcPctQuotaUsed(pUser[0], &dwPct[0]);
  1692. CalcPctQuotaUsed(pUser[1], &dwPct[1]);
  1693. diff = (INT)dwPct[ i[0] ] - (INT)dwPct[ i[1] ];
  1694. break;
  1695. }
  1696. default:
  1697. break;
  1698. }
  1699. //
  1700. // Translate return value to -1, 0 or 1.
  1701. //
  1702. INT iReturn = 0;
  1703. if (0 != diff)
  1704. {
  1705. if (0 < diff)
  1706. iReturn = 1;
  1707. else
  1708. iReturn = -1;
  1709. }
  1710. return iReturn;
  1711. }
  1712. ///////////////////////////////////////////////////////////////////////////////
  1713. /* Function: DetailsView::WndProc
  1714. Description: Window procedure for the details view main window. This
  1715. method merely dispatches the messages to other methods that do the
  1716. actual work. These work methods should be declared "inline".
  1717. Arguments: Standard WndProc arguments.
  1718. Returns:
  1719. Revision History:
  1720. Date Description Programmer
  1721. -------- --------------------------------------------------- ----------
  1722. 08/20/96 Initial creation. BrianAu
  1723. */
  1724. ///////////////////////////////////////////////////////////////////////////////
  1725. LRESULT CALLBACK
  1726. DetailsView::WndProc(
  1727. HWND hWnd,
  1728. UINT message,
  1729. WPARAM wParam,
  1730. LPARAM lParam)
  1731. {
  1732. //
  1733. // Retrieve the DetailsView object's "this" pointer from the window's
  1734. // USERDATA.
  1735. //
  1736. DetailsView *pThis = (DetailsView *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  1737. try
  1738. {
  1739. switch(message)
  1740. {
  1741. case WM_CREATE:
  1742. {
  1743. CREATESTRUCT *pcs = (CREATESTRUCT *)lParam;
  1744. PWNDCREATE_DATA pCreateData = (PWNDCREATE_DATA)(pcs->lpCreateParams);
  1745. DBGASSERT((NULL != pCreateData));
  1746. pThis = (DetailsView *)(pCreateData->pThis);
  1747. DBGASSERT((NULL != pThis));
  1748. SetWindowLongPtr(hWnd, GWLP_USERDATA, (INT_PTR)pThis);
  1749. InitCommonControls();
  1750. return 0;
  1751. }
  1752. case WM_COMMAND:
  1753. DBGASSERT((NULL != pThis));
  1754. pThis->OnCommand(hWnd, message, wParam, lParam);
  1755. //
  1756. // Enable the "Undo" menu if the undo list is not empty.
  1757. //
  1758. pThis->EnableMenuItem_Undo(0 != pThis->m_pUndoList->Count());
  1759. return 0;
  1760. case WM_CONTEXTMENU:
  1761. DBGASSERT((NULL != pThis));
  1762. pThis->OnContextMenu(hWnd, message, wParam, lParam);
  1763. return 0;
  1764. case WM_CLOSE:
  1765. case WM_ENDSESSION:
  1766. DestroyWindow(hWnd);
  1767. return 0;
  1768. case WM_DESTROY:
  1769. DBGASSERT((NULL != pThis));
  1770. pThis->OnDestroy(hWnd, message, wParam, lParam);
  1771. return 0;
  1772. case WM_ADD_USER_TO_DETAILS_VIEW: // This is DSKQUOTA-specific.
  1773. DBGASSERT((NULL != pThis));
  1774. pThis->AddUser((PDISKQUOTA_USER)lParam);
  1775. return 0;
  1776. case WM_MAINWINDOW_CREATED: // This is DSKQUOTA-specific.
  1777. DBGASSERT((NULL != pThis));
  1778. pThis->OnMainWindowCreated(hWnd, message, wParam, lParam);
  1779. return 0;
  1780. case WM_MENUSELECT:
  1781. DBGASSERT((NULL != pThis));
  1782. pThis->OnMenuSelect(hWnd, message, wParam, lParam);
  1783. return 0;
  1784. case WM_NOTIFY:
  1785. DBGASSERT((NULL != pThis));
  1786. pThis->OnNotify(hWnd, message, wParam, lParam);
  1787. return 0;
  1788. case WM_SETFOCUS:
  1789. DBGASSERT((NULL != pThis));
  1790. pThis->OnSetFocus(hWnd, message, wParam, lParam);
  1791. return 0;
  1792. case WM_SIZE:
  1793. DBGASSERT((NULL != pThis));
  1794. pThis->OnSize(hWnd, message, wParam, lParam);
  1795. return 0;
  1796. case WM_SYSCOLORCHANGE:
  1797. case WM_SETTINGCHANGE:
  1798. DBGASSERT((NULL != pThis));
  1799. pThis->OnSettingChange(hWnd, message, wParam, lParam);
  1800. return 0;
  1801. default:
  1802. break;
  1803. }
  1804. }
  1805. catch(CAllocException& e)
  1806. {
  1807. //
  1808. // Handle out-of-memory errors here. Any other exceptions
  1809. // can be thrown to caller. Let ThreadProc handle them.
  1810. //
  1811. DiskQuotaMsgBox(GetDesktopWindow(),
  1812. IDS_OUTOFMEMORY,
  1813. IDS_TITLE_DISK_QUOTA,
  1814. MB_ICONERROR | MB_OK);
  1815. }
  1816. return DefWindowProc(hWnd, message, wParam, lParam);
  1817. }
  1818. ///////////////////////////////////////////////////////////////////////////////
  1819. /* Function: DetailsView::LVSubClassWndProc
  1820. Description: Window proc for the sub-classed listview control.
  1821. This is required so that we can intecept mouse messages and respond
  1822. to the request for tooltip text.
  1823. Arguments: Std windows WndProc args.
  1824. Returns:
  1825. Revision History:
  1826. Date Description Programmer
  1827. -------- --------------------------------------------------- ----------
  1828. 09/09/96 Initial creation. BrianAu
  1829. */
  1830. ///////////////////////////////////////////////////////////////////////////////
  1831. LRESULT CALLBACK
  1832. DetailsView::LVSubClassWndProc(
  1833. HWND hWnd,
  1834. UINT message,
  1835. WPARAM wParam,
  1836. LPARAM lParam
  1837. )
  1838. {
  1839. DetailsView *pThis = (DetailsView *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  1840. switch(message)
  1841. {
  1842. case WM_NOTIFY:
  1843. {
  1844. //
  1845. // Only return ToolTip text if TTN_NEEDTEXT is being
  1846. // sent from our tooltip. Don't respond to the listview's
  1847. // internal tooltip's request for text.
  1848. //
  1849. LV_DISPINFO *pDispInfo = (LV_DISPINFO *)lParam;
  1850. if (pDispInfo->hdr.hwndFrom == pThis->m_hwndListViewToolTip)
  1851. {
  1852. switch(pDispInfo->hdr.code)
  1853. {
  1854. case TTN_NEEDTEXT:
  1855. //
  1856. // FEATURE: With the removal of the "domain" term from the UI, I
  1857. // decided we don't need this tooltip any more.
  1858. // However, I'm making this change in the last hour before
  1859. // "code complete" and I don't want to break something else.
  1860. // Therefore I'm just commenting this out and leaving the
  1861. // subclassing in place. If there's time later, this subclassing
  1862. // of the listview should be removed. [brianau - 03/19/98]
  1863. //
  1864. // pThis->LV_OnTTN_NeedText((TOOLTIPTEXT *)lParam);
  1865. return 0;
  1866. default:
  1867. break;
  1868. }
  1869. }
  1870. }
  1871. break;
  1872. case WM_MOUSEMOVE:
  1873. DBGASSERT((NULL != pThis));
  1874. pThis->LV_OnMouseMessages(hWnd, message, wParam, lParam);
  1875. break;
  1876. case WM_ADD_USER_TO_DETAILS_VIEW: // This is DSKQUOTA-specific.
  1877. DBGASSERT((NULL != pThis));
  1878. pThis->AddUser((PDISKQUOTA_USER)lParam);
  1879. break;
  1880. default:
  1881. break;
  1882. }
  1883. DBGASSERT((NULL != pThis->m_lpfnLVWndProc));
  1884. return CallWindowProc(pThis->m_lpfnLVWndProc, hWnd, message, wParam, lParam);
  1885. }
  1886. ///////////////////////////////////////////////////////////////////////////////
  1887. /* Function: DetailsView::OnCommand
  1888. Description: Handler for WM_COMMAND.
  1889. Arguments: Standard WndProc arguments.
  1890. Returns:
  1891. Revision History:
  1892. Date Description Programmer
  1893. -------- --------------------------------------------------- ----------
  1894. 08/20/96 Initial creation. BrianAu
  1895. 09/06/96 Added "Show Domain" menu option. BrianAu
  1896. 05/20/97 Added IDM_EDIT_FIND and IDM_EDIT_FIND_LIST. BrianAu
  1897. */
  1898. ///////////////////////////////////////////////////////////////////////////////
  1899. LRESULT
  1900. DetailsView::OnCommand(
  1901. HWND hWnd,
  1902. UINT message,
  1903. WPARAM wParam,
  1904. LPARAM lParam
  1905. )
  1906. {
  1907. switch(LOWORD(wParam))
  1908. {
  1909. case IDM_EDIT_INVERTSELECTION:
  1910. InvertSelectedItems();
  1911. break;
  1912. case IDM_EDIT_UNDO:
  1913. OnCmdUndo();
  1914. break;
  1915. case IDM_EDIT_FIND:
  1916. OnCmdFind();
  1917. break;
  1918. case IDM_EDIT_FIND_LIST:
  1919. SetFocus(m_hwndToolbarCombo);
  1920. break;
  1921. case IDM_EDIT_SELECTALL:
  1922. SelectAllItems();
  1923. break;
  1924. case IDM_EDIT_COPY:
  1925. OnCmdEditCopy();
  1926. break;
  1927. case IDM_HELP_ABOUT:
  1928. OnHelpAbout(hWnd);
  1929. break;
  1930. case IDM_HELP_TOPICS:
  1931. OnHelpTopics(hWnd);
  1932. break;
  1933. case IDM_QUOTA_CLOSE:
  1934. DestroyWindow(m_hwndMain);
  1935. break;
  1936. case IDM_QUOTA_DELETE:
  1937. OnCmdDelete();
  1938. FocusOnSomething(); // Needed if DEL key pressed.
  1939. break;
  1940. case IDM_QUOTA_NEW:
  1941. OnCmdNew();
  1942. break;
  1943. case IDM_QUOTA_PROPERTIES:
  1944. OnCmdProperties();
  1945. break;
  1946. case IDM_QUOTA_IMPORT:
  1947. OnCmdImport();
  1948. break;
  1949. case IDM_QUOTA_EXPORT:
  1950. OnCmdExport();
  1951. break;
  1952. case IDM_VIEW_ARRANGE_BYFOLDER:
  1953. SortObjects(DetailsView::idCol_Folder, m_fSortDirection);
  1954. break;
  1955. case IDM_VIEW_ARRANGE_BYLIMIT:
  1956. SortObjects(DetailsView::idCol_Limit, m_fSortDirection);
  1957. break;
  1958. case IDM_VIEW_ARRANGE_BYNAME:
  1959. SortObjects(DetailsView::idCol_Name, m_fSortDirection);
  1960. break;
  1961. case IDM_VIEW_ARRANGE_BYLOGONNAME:
  1962. SortObjects(DetailsView::idCol_LogonName, m_fSortDirection);
  1963. break;
  1964. case IDM_VIEW_ARRANGE_BYPERCENT:
  1965. SortObjects(DetailsView::idCol_PctUsed, m_fSortDirection);
  1966. break;
  1967. case IDM_VIEW_ARRANGE_BYTHRESHOLD:
  1968. SortObjects(DetailsView::idCol_Threshold, m_fSortDirection);
  1969. break;
  1970. case IDM_VIEW_ARRANGE_BYSTATUS:
  1971. SortObjects(DetailsView::idCol_Status, m_fSortDirection);
  1972. break;
  1973. case IDM_VIEW_ARRANGE_BYUSED:
  1974. SortObjects(DetailsView::idCol_AmtUsed, m_fSortDirection);
  1975. break;
  1976. case IDM_VIEW_REFRESH:
  1977. Refresh(true);
  1978. break;
  1979. case IDM_VIEW_STATUSBAR:
  1980. OnCmdViewStatusBar();
  1981. break;
  1982. case IDM_VIEW_TOOLBAR:
  1983. OnCmdViewToolBar();
  1984. break;
  1985. case IDM_VIEW_SHOWFOLDER:
  1986. OnCmdViewShowFolder();
  1987. break;
  1988. //
  1989. // These are just for development.
  1990. //
  1991. // case IDM_CLEAR_CACHE:
  1992. // m_pQuotaControl->InvalidateSidNameCache();
  1993. // break;
  1994. default:
  1995. break;
  1996. }
  1997. return 0;
  1998. }
  1999. LRESULT
  2000. DetailsView::OnSettingChange(
  2001. HWND hwnd,
  2002. UINT uMsg,
  2003. WPARAM wParam,
  2004. LPARAM lParam
  2005. )
  2006. {
  2007. HWND rghwnd[] = { m_hwndListView,
  2008. m_hwndStatusBar,
  2009. m_hwndToolBar,
  2010. m_hwndToolbarCombo,
  2011. m_hwndListViewToolTip,
  2012. m_hwndHeader };
  2013. for (int i = 0; i < ARRAYSIZE(rghwnd); i++)
  2014. {
  2015. SendMessage(rghwnd[i], uMsg, wParam, lParam);
  2016. }
  2017. return 0;
  2018. }
  2019. //
  2020. // Is an x,y screen position in the LV header control?
  2021. //
  2022. BOOL
  2023. DetailsView::HitTestHeader(
  2024. int xPos,
  2025. int yPos
  2026. )
  2027. {
  2028. RECT rcHdr;
  2029. POINT pt = { xPos, yPos };
  2030. GetWindowRect(m_hwndHeader, &rcHdr);
  2031. return PtInRect(&rcHdr, pt);
  2032. }
  2033. ///////////////////////////////////////////////////////////////////////////////
  2034. /* Function: DetailsView::OnContextMenu
  2035. Description: Handler for WM_CONTEXTMENU.
  2036. Creates and tracks a popup context menu for deleting
  2037. selected object(s) and showing their properties.
  2038. Arguments: Standard WndProc arguments.
  2039. Returns:
  2040. Revision History:
  2041. Date Description Programmer
  2042. -------- --------------------------------------------------- ----------
  2043. 08/20/96 Initial creation. BrianAu
  2044. */
  2045. ///////////////////////////////////////////////////////////////////////////////
  2046. LRESULT
  2047. DetailsView::OnContextMenu(
  2048. HWND hWnd,
  2049. UINT message,
  2050. WPARAM wParam,
  2051. LPARAM lParam
  2052. )
  2053. {
  2054. //
  2055. // Only display menu if the message is from the list view and there's
  2056. // one or more objects selected in the list view.
  2057. //
  2058. if ((HWND)wParam == m_hwndListView &&
  2059. !HitTestHeader(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)) &&
  2060. ListView_GetSelectedCount(m_hwndListView) > 0)
  2061. {
  2062. HMENU hMenu = LoadMenu(g_hInstDll, MAKEINTRESOURCE(IDM_CONTEXT_MENU));
  2063. if (NULL != hMenu)
  2064. {
  2065. HMENU hMenuTrackPopup = GetSubMenu(hMenu, 0);
  2066. SetMenuDefaultItem(hMenuTrackPopup, IDM_QUOTA_PROPERTIES, MF_BYCOMMAND);
  2067. if (LPARAM(-1) == lParam)
  2068. {
  2069. //
  2070. // Invoked from keyboard. Place menu at focused item.
  2071. //
  2072. POINT pt = { -1, -1 };
  2073. int i = ListView_GetNextItem(m_hwndListView, -1, LVNI_FOCUSED);
  2074. if (i != -1)
  2075. {
  2076. ListView_GetItemPosition(m_hwndListView, i, &pt);
  2077. ClientToScreen(m_hwndListView, &pt);
  2078. }
  2079. lParam = MAKELPARAM(pt.x, pt.y);
  2080. }
  2081. if (LPARAM(-1) != lParam)
  2082. {
  2083. TrackPopupMenu(hMenuTrackPopup,
  2084. TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  2085. GET_X_LPARAM(lParam),
  2086. GET_Y_LPARAM(lParam),
  2087. 0,
  2088. hWnd,
  2089. NULL);
  2090. }
  2091. DestroyMenu(hMenu);
  2092. }
  2093. }
  2094. return 0;
  2095. }
  2096. ///////////////////////////////////////////////////////////////////////////////
  2097. /* Function: DetailsView::OnDestroy
  2098. Description: Handler for WM_DESTROY.
  2099. Arguments: Standard WndProc arguments.
  2100. Returns:
  2101. Revision History:
  2102. Date Description Programmer
  2103. -------- --------------------------------------------------- ----------
  2104. 08/20/96 Initial creation. BrianAu
  2105. */
  2106. ///////////////////////////////////////////////////////////////////////////////
  2107. LRESULT
  2108. DetailsView::OnDestroy(
  2109. HWND hWnd,
  2110. UINT message,
  2111. WPARAM wParam,
  2112. LPARAM lParam
  2113. )
  2114. {
  2115. //
  2116. // We don't want to destroy the view window while a user-name-changed
  2117. // update is in progress. Likewise, we don't want to perform a name
  2118. // update if the view is being (or has been) destroyed. The crit sec
  2119. // m_csAsyncUpdate and the flag m_bDestroyingView work together to
  2120. // ensure this.
  2121. //
  2122. EnterCriticalSection(&m_csAsyncUpdate);
  2123. m_bDestroyingView = TRUE; // Destruction of DetailsView in progress.
  2124. m_bStopLoadingObjects = TRUE; // Will terminate in-progress loading.
  2125. //
  2126. // Unregister the main window as an OLE drop target.
  2127. //
  2128. if (NULL != hWnd)
  2129. {
  2130. RegisterAsDropTarget(FALSE);
  2131. }
  2132. //
  2133. // Cancel subclassing of the listview control.
  2134. //
  2135. if (NULL != m_lpfnLVWndProc)
  2136. SetWindowLongPtr(m_hwndListView, GWLP_WNDPROC, (INT_PTR)m_lpfnLVWndProc);
  2137. DisconnectEventSink();
  2138. if (NULL != m_pQuotaControl)
  2139. {
  2140. m_pQuotaControl->Release();
  2141. m_pQuotaControl = NULL;
  2142. }
  2143. //
  2144. // Save the view dimensions and column widths to the registry.
  2145. // We want the user to be able to configure the view and leave it.
  2146. //
  2147. SaveViewStateToRegistry();
  2148. //
  2149. // If we have a data object on the clipboard, clear the clipboard.
  2150. // Note that the clipboard holds the reference to the data object.
  2151. // When we clear the clipboard, the data object will be released.
  2152. //
  2153. if (NULL != m_pIDataObjectOnClipboard &&
  2154. S_OK == OleIsCurrentClipboard(m_pIDataObjectOnClipboard))
  2155. {
  2156. OleFlushClipboard();
  2157. }
  2158. //
  2159. // All done now. Post a WM_QUIT message to the thread to tell
  2160. // it to exit. On termination, the thread proc will release
  2161. // the view object, calling the destructor.
  2162. //
  2163. PostMessage(hWnd, WM_QUIT, 0, 0);
  2164. LeaveCriticalSection(&m_csAsyncUpdate);
  2165. return 0;
  2166. }
  2167. ///////////////////////////////////////////////////////////////////////////////
  2168. /* Function: DetailsView::RegisterAsDropTarget
  2169. Description: Registers or De-Registers the details view window as
  2170. an OLE drop target.
  2171. Arguments:
  2172. bActive - If TRUE, registers as a drop target.
  2173. If FALSE, un-registers as a drop target.
  2174. Returns: Nothing.
  2175. Revision History:
  2176. Date Description Programmer
  2177. -------- --------------------------------------------------- ----------
  2178. 05/28/97 Initial creation. BrianAu
  2179. */
  2180. ///////////////////////////////////////////////////////////////////////////////
  2181. VOID
  2182. DetailsView::RegisterAsDropTarget(
  2183. BOOL bActive
  2184. )
  2185. {
  2186. if (bActive)
  2187. {
  2188. //
  2189. // Register as a drop target.
  2190. //
  2191. CoLockObjectExternal(static_cast<IDropTarget *>(this), TRUE, FALSE);
  2192. RegisterDragDrop(m_hwndMain, static_cast<IDropTarget *>(this));
  2193. }
  2194. else
  2195. {
  2196. //
  2197. // Un-register as a drop target.
  2198. //
  2199. RevokeDragDrop(m_hwndMain);
  2200. CoLockObjectExternal(static_cast<IDropTarget *>(this), FALSE, TRUE);
  2201. }
  2202. }
  2203. ///////////////////////////////////////////////////////////////////////////////
  2204. /* Function: DetailsView::SaveViewStateToRegistry
  2205. Description: Saves the listview height/width and the column widths to
  2206. the registry. When the listview is created, these values are used
  2207. to size it so that the user doesn't always have to re-configure the
  2208. view every time they open it. Also saves the visibility state of the
  2209. toolbar, statusbar and folder column.
  2210. Arguments: None.
  2211. Returns: Nothing.
  2212. Revision History:
  2213. Date Description Programmer
  2214. -------- --------------------------------------------------- ----------
  2215. 09/25/96 Initial creation. BrianAu
  2216. 05/20/97 Added FindMRU list to persistent reg data. BrianAu
  2217. */
  2218. ///////////////////////////////////////////////////////////////////////////////
  2219. VOID
  2220. DetailsView::SaveViewStateToRegistry(
  2221. VOID
  2222. )
  2223. {
  2224. RECT rc;
  2225. HDC hdc = GetDC(m_hwndMain);
  2226. RegKey keyPref(HKEY_CURRENT_USER, REGSTR_KEY_DISKQUOTA);
  2227. if (FAILED(keyPref.Open(KEY_WRITE, true)))
  2228. {
  2229. DBGERROR((TEXT("Error opening reg key \"%s\""), REGSTR_KEY_DISKQUOTA));
  2230. return;
  2231. }
  2232. m_lvsi.cb = sizeof(LV_STATE_INFO);
  2233. //
  2234. // Save current screen resolution.
  2235. //
  2236. m_lvsi.cxScreen = (WORD)GetDeviceCaps(hdc, HORZRES);
  2237. m_lvsi.cyScreen = (WORD)GetDeviceCaps(hdc, VERTRES);
  2238. ReleaseDC(m_hwndMain, hdc);
  2239. //
  2240. // Save current listview window size.
  2241. //
  2242. GetWindowRect(m_hwndMain, &rc);
  2243. m_lvsi.cx = rc.right - rc.left;
  2244. m_lvsi.cy = rc.bottom - rc.top;
  2245. //
  2246. // Save listview column widths.
  2247. //
  2248. UINT cColumns = Header_GetItemCount(m_hwndHeader);
  2249. for (UINT i = 0; i < cColumns; i++)
  2250. {
  2251. m_lvsi.rgcxCol[i] = ListView_GetColumnWidth(m_hwndListView, i);
  2252. }
  2253. //
  2254. // Save the current order of the columns in the listview.
  2255. //
  2256. DBGASSERT(cColumns <= ARRAYSIZE(m_lvsi.rgColIndices));
  2257. Header_GetOrderArray(m_hwndHeader, cColumns, m_lvsi.rgColIndices);
  2258. //
  2259. // Save column sorting state.
  2260. // Casts are because we use a WORD bit field in the LVSI structure.
  2261. //
  2262. m_lvsi.iLastColSorted = (WORD)(m_iLastColSorted & 0xF); // Uses only lower 4 bits.
  2263. m_lvsi.fSortDirection = (WORD)m_fSortDirection;
  2264. //
  2265. // Write preference data to registry.
  2266. //
  2267. keyPref.SetValue(REGSTR_VAL_PREFERENCES, (LPBYTE)&m_lvsi, m_lvsi.cb);
  2268. //
  2269. // Save the contents of the Find MRU list.
  2270. //
  2271. UINT cNames = (UINT)SendMessage(m_hwndToolbarCombo, CB_GETCOUNT, 0, 0);
  2272. if (CB_ERR != cNames && 0 < cNames)
  2273. {
  2274. CArray<CString> rgstrNames(cNames);
  2275. for (i = 0; i < cNames; i++)
  2276. {
  2277. INT cchName = (INT)SendMessage(m_hwndToolbarCombo, CB_GETLBTEXTLEN, i, 0);
  2278. if (CB_ERR != cchName && 0 < cchName)
  2279. {
  2280. CString s;
  2281. cchName = (INT)SendMessage(m_hwndToolbarCombo, CB_GETLBTEXT, i, (LPARAM)s.GetBuffer(cchName + 1));
  2282. s.ReleaseBuffer();
  2283. if (CB_ERR != cchName)
  2284. {
  2285. rgstrNames[i] = s;
  2286. }
  2287. }
  2288. }
  2289. keyPref.SetValue(REGSTR_VAL_FINDMRU, rgstrNames);
  2290. }
  2291. }
  2292. ///////////////////////////////////////////////////////////////////////////////
  2293. /* Function: DetailsView::OnMainWindowCreated
  2294. Description: Handles WM_MAIN_WINDOW_CREATED.
  2295. This message is posted by ThreadProc after main window creation is
  2296. complete. It does all the stuff to get the window up and running.
  2297. Arguments: Standard WndProc arguments.
  2298. Returns:
  2299. Revision History:
  2300. Date Description Programmer
  2301. -------- --------------------------------------------------- ----------
  2302. 08/20/96 Initial creation. BrianAu
  2303. */
  2304. ///////////////////////////////////////////////////////////////////////////////
  2305. LRESULT
  2306. DetailsView::OnMainWindowCreated(
  2307. HWND hWnd,
  2308. UINT message,
  2309. WPARAM wParam,
  2310. LPARAM lParam
  2311. )
  2312. {
  2313. DBGASSERT((NULL != m_hwndMain));
  2314. //
  2315. // The accelerator table is automatically freed by Windows
  2316. // when the app terminates.
  2317. //
  2318. m_hKbdAccel = LoadAccelerators(g_hInstDll,
  2319. MAKEINTRESOURCE(IDR_KBDACCEL));
  2320. CreateListView();
  2321. CreateStatusBar();
  2322. CreateToolBar();
  2323. ConnectEventSink();
  2324. ShowWindow(m_hwndMain, SW_SHOWNORMAL);
  2325. UpdateWindow(m_hwndMain);
  2326. //
  2327. // Create the UNDO object.
  2328. //
  2329. m_pUndoList = new UndoList(&m_UserList, m_hwndListView);
  2330. ShowItemCountInStatusBar();
  2331. Refresh();
  2332. return 0;
  2333. }
  2334. ///////////////////////////////////////////////////////////////////////////////
  2335. /* Function: DetailsView::OnMenuSelect
  2336. Description: Handles WM_MENUSELECT.
  2337. If a menu item is currently selected AND the status bar is visible,
  2338. the menu item's description is displayed in the status bar. When
  2339. the menu is closed, the status bar reverts back to an item count.
  2340. Arguments: Standard WndProc arguments.
  2341. Returns:
  2342. Revision History:
  2343. Date Description Programmer
  2344. -------- --------------------------------------------------- ----------
  2345. 08/20/96 Initial creation. BrianAu
  2346. */
  2347. ///////////////////////////////////////////////////////////////////////////////
  2348. LRESULT
  2349. DetailsView::OnMenuSelect(
  2350. HWND hWnd,
  2351. UINT message,
  2352. WPARAM wParam,
  2353. LPARAM lParam
  2354. )
  2355. {
  2356. if (0xFFFF == HIWORD(wParam) && NULL == (HMENU)lParam)
  2357. {
  2358. //
  2359. // Menu closed.
  2360. //
  2361. m_bMenuActive = FALSE;
  2362. ShowItemCountInStatusBar();
  2363. }
  2364. else
  2365. {
  2366. //
  2367. // Item selected.
  2368. //
  2369. m_bMenuActive = TRUE;
  2370. ShowMenuTextInStatusBar(LOWORD(wParam));
  2371. }
  2372. return 0;
  2373. }
  2374. ///////////////////////////////////////////////////////////////////////////////
  2375. /* Function: DetailsView::OnNotify
  2376. Description: Handles all LVN_XXXXXX list view control notifications.
  2377. Dispatches specific notifications to other handlers.
  2378. Arguments: Standard WndProc arguments.
  2379. Returns:
  2380. Revision History:
  2381. Date Description Programmer
  2382. -------- --------------------------------------------------- ----------
  2383. 08/20/96 Initial creation. BrianAu
  2384. */
  2385. ///////////////////////////////////////////////////////////////////////////////
  2386. LRESULT
  2387. DetailsView::OnNotify(
  2388. HWND hWnd,
  2389. UINT message,
  2390. WPARAM wParam,
  2391. LPARAM lParam
  2392. )
  2393. {
  2394. NMHDR *pnmhdr = (NMHDR *)lParam;
  2395. switch(pnmhdr->code)
  2396. {
  2397. case NM_DBLCLK:
  2398. case NM_RETURN:
  2399. OnCmdProperties( );
  2400. //
  2401. // Enable/disable Undo menu item.
  2402. //
  2403. EnableMenuItem_Undo(0 != m_pUndoList->Count());
  2404. break;
  2405. case NM_SETFOCUS:
  2406. FocusOnSomething(); // Something should always be highlighted.
  2407. break;
  2408. case LVN_ODFINDITEM:
  2409. OnLVN_OwnerDataFindItem((NMLVFINDITEM *)lParam);
  2410. break;
  2411. case LVN_GETDISPINFO:
  2412. OnLVN_GetDispInfo((LV_DISPINFO *)lParam);
  2413. break;
  2414. case LVN_BEGINDRAG:
  2415. OnLVN_BeginDrag((NM_LISTVIEW *)lParam);
  2416. break;
  2417. case LVN_COLUMNCLICK:
  2418. OnLVN_ColumnClick((NM_LISTVIEW *)lParam);
  2419. break;
  2420. case LVN_ITEMCHANGED:
  2421. OnLVN_ItemChanged((NM_LISTVIEW *)lParam);
  2422. break;
  2423. case TTN_NEEDTEXT:
  2424. OnTTN_NeedText((TOOLTIPTEXT *)lParam);
  2425. //
  2426. // Fall through.
  2427. //
  2428. default:
  2429. break;
  2430. }
  2431. return 0;
  2432. }
  2433. ///////////////////////////////////////////////////////////////////////////////
  2434. /* Function: DetailsView::LV_OnMouseMessages
  2435. Description: Handles mouse messages for the subclassed listview control.
  2436. These must be intercepted so that we can...
  2437. a) Tell the tooltip when we've hit another listview item.
  2438. and b) Forward all mouse messages to the tooltip window.
  2439. Arguments: Standard WndProc args.
  2440. Returns:
  2441. Revision History:
  2442. Date Description Programmer
  2443. -------- --------------------------------------------------- ----------
  2444. 09/09/96 Initial creation. BrianAu
  2445. */
  2446. ///////////////////////////////////////////////////////////////////////////////
  2447. LRESULT
  2448. DetailsView::LV_OnMouseMessages(
  2449. HWND hWnd,
  2450. UINT message,
  2451. WPARAM wParam,
  2452. LPARAM lParam
  2453. )
  2454. {
  2455. switch(message)
  2456. {
  2457. case WM_MOUSEMOVE:
  2458. {
  2459. //
  2460. // If we've moved the mouse to another listview item,
  2461. // make the tooltip window think we're over another tool.
  2462. // The tooltip window thinks the entire listview is a single
  2463. // tool but we want to treat each item as a separate tool.
  2464. // Note that m_ptMouse.x and .y are recorded when the main
  2465. // window receives WM_MOUSEMOVE.
  2466. //
  2467. LV_HITTESTINFO hti;
  2468. INT iItem = 0;
  2469. hti.pt.x = m_ptMouse.x = GET_X_LPARAM(lParam);
  2470. hti.pt.y = m_ptMouse.y = GET_Y_LPARAM(lParam);
  2471. if (-1 != (iItem = ListView_HitTest(m_hwndListView, &hti)))
  2472. {
  2473. if (iItem != m_iLastItemHit)
  2474. {
  2475. SendMessage(m_hwndListViewToolTip, WM_MOUSEMOVE, 0, 0);
  2476. m_iLastItemHit = iItem;
  2477. }
  2478. }
  2479. else
  2480. {
  2481. ShowWindow(m_hwndListViewToolTip, SW_HIDE);
  2482. m_iLastItemHit = iItem;
  2483. }
  2484. }
  2485. //
  2486. // Fall through.
  2487. //
  2488. case WM_LBUTTONUP:
  2489. case WM_LBUTTONDOWN:
  2490. case WM_MBUTTONUP:
  2491. case WM_MBUTTONDOWN:
  2492. case WM_RBUTTONUP:
  2493. case WM_RBUTTONDOWN:
  2494. {
  2495. //
  2496. // Relay all mouse messages to the listview's tooltip control.
  2497. //
  2498. MSG msg;
  2499. msg.hwnd = hWnd;
  2500. msg.message = message;
  2501. msg.wParam = wParam;
  2502. msg.lParam = lParam;
  2503. SendMessage(m_hwndListViewToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
  2504. }
  2505. break;
  2506. default:
  2507. break;
  2508. }
  2509. return 0;
  2510. }
  2511. ///////////////////////////////////////////////////////////////////////////////
  2512. /* Function: DetailsView::OnCmdEditCopy
  2513. Description: Handles WM_COMMAND, IDM_EDIT_COPY. This is invoked whenever
  2514. a user selects the Copy menu item or presses Ctrl + C. The method
  2515. creates a DataObject (same used in drag-drop) and places it on the
  2516. OLE clipboard. The data is rendered when OLE asks for it via
  2517. IDataObject::GetData.
  2518. Arguments: None.
  2519. Returns: Always 0.
  2520. Revision History:
  2521. Date Description Programmer
  2522. -------- --------------------------------------------------- ----------
  2523. 09/09/96 Initial creation. BrianAu
  2524. */
  2525. ///////////////////////////////////////////////////////////////////////////////
  2526. LRESULT
  2527. DetailsView::OnCmdEditCopy(
  2528. VOID
  2529. )
  2530. {
  2531. HRESULT hResult = NO_ERROR;
  2532. hResult = QueryInterface(IID_IDataObject, (LPVOID *)&m_pIDataObjectOnClipboard);
  2533. if (SUCCEEDED(hResult))
  2534. {
  2535. OleSetClipboard(m_pIDataObjectOnClipboard);
  2536. //
  2537. // OLE calls AddRef() so we can release the count added in QI.
  2538. //
  2539. m_pIDataObjectOnClipboard->Release();
  2540. }
  2541. return 0;
  2542. }
  2543. ///////////////////////////////////////////////////////////////////////////////
  2544. /* Function: DetailsView::OnLVN_BeginDrag
  2545. Description: Called when the user has selected one or more items in the
  2546. listview and begins a drag operation. Creates a DropSource object
  2547. and a DataObject then calls DoDragDrop() to execute the drag-drop
  2548. operation.
  2549. Note that we don't store the IDataObject pointer in
  2550. m_IDataObjectOnClipboard. That member is only for clipboard copy
  2551. operations, not drag/drop.
  2552. Arguments:
  2553. pNm - Address of notification message structure.
  2554. Returns: TRUE = Succeeded.
  2555. FALSE = Failed.
  2556. Revision History:
  2557. Date Description Programmer
  2558. -------- --------------------------------------------------- ----------
  2559. 09/09/96 Initial creation. BrianAu
  2560. */
  2561. ///////////////////////////////////////////////////////////////////////////////
  2562. LRESULT
  2563. DetailsView::OnLVN_BeginDrag(
  2564. NM_LISTVIEW *pNm
  2565. )
  2566. {
  2567. DBGTRACE((DM_DRAGDROP, DL_HIGH, TEXT("DetailsView::OnLVN_BeginDragDrop")));
  2568. HRESULT hResult = NO_ERROR;
  2569. IDataObject *pIDataObject = NULL;
  2570. IDropSource *pIDropSource = NULL;
  2571. DBGPRINT((DM_DRAGDROP, DL_HIGH, TEXT("DRAGDROP - Beginning Drag/Drop")));
  2572. try
  2573. {
  2574. hResult = QueryInterface(IID_IDataObject, (LPVOID *)&pIDataObject);
  2575. if (SUCCEEDED(hResult))
  2576. {
  2577. hResult = QueryInterface(IID_IDropSource, (LPVOID *)&pIDropSource);
  2578. if (SUCCEEDED(hResult))
  2579. {
  2580. DWORD dwEffect = 0;
  2581. //
  2582. // Unregister our window as a drop target while we're acting as a drop
  2583. // source. Don't want to drop our own data onto our own window.
  2584. //
  2585. RegisterAsDropTarget(FALSE);
  2586. hResult = DoDragDrop(pIDataObject,
  2587. pIDropSource,
  2588. DROPEFFECT_COPY | DROPEFFECT_MOVE,
  2589. &dwEffect);
  2590. //
  2591. // FEATURE: Should probably display some error UI here.
  2592. // The shell doesn't indicate any error if the
  2593. // destination volume is full or if there's a write
  2594. // error. The only indication of a failure is that
  2595. // dwEffect will contain 0. We could display something
  2596. // like "An error occured while transferring the selected
  2597. // items." The big problem is that only the shell knows
  2598. // where the data was stored so only it could delete
  2599. // the created file. Displaying a message but leaving
  2600. // the file is also confusing. [brianau 7/29/97]
  2601. // NT Bug 96282 will fix the shell not deleting the file.
  2602. //
  2603. RegisterAsDropTarget(TRUE);
  2604. DBGPRINT((TEXT("DRAGDROP - Drag/Drop complete.\n\t hResult = 0x%08X Effect = 0x%08X"),
  2605. hResult, dwEffect));
  2606. pIDropSource->Release();
  2607. pIDropSource = NULL;
  2608. }
  2609. pIDataObject->Release();
  2610. pIDataObject = NULL;
  2611. }
  2612. }
  2613. catch(CAllocException& e)
  2614. {
  2615. if (NULL != pIDropSource)
  2616. pIDropSource->Release();
  2617. if (NULL != pIDataObject)
  2618. pIDataObject->Release();
  2619. RegisterAsDropTarget(TRUE);
  2620. throw;
  2621. }
  2622. return SUCCEEDED(hResult);
  2623. }
  2624. ///////////////////////////////////////////////////////////////////////////////
  2625. /* Function: DetailsView::OnTTN_NeedText
  2626. Description: Handles requests for tooltip text for the main window's tool
  2627. bar buttons.
  2628. Arguments:
  2629. pToolTipText - Address of tooltip text notification information.
  2630. Returns: Always returns 0.
  2631. Revision History:
  2632. Date Description Programmer
  2633. -------- --------------------------------------------------- ----------
  2634. 09/09/96 Initial creation. BrianAu
  2635. 07/09/97 Added cmd/TT cross reference. BrianAu
  2636. Previously used the tool status text in tooltip.
  2637. */
  2638. ///////////////////////////////////////////////////////////////////////////////
  2639. LRESULT
  2640. DetailsView::OnTTN_NeedText(
  2641. TOOLTIPTEXT *pToolTipText
  2642. )
  2643. {
  2644. //
  2645. // Cross-reference tool command IDs with tooltip text IDs.
  2646. //
  2647. const struct
  2648. {
  2649. UINT idCmd; // Tool cmd ID.
  2650. UINT idTT; // Tooltip text ID.
  2651. } CmdTTXRef[] = {
  2652. { IDM_QUOTA_NEW, IDS_TT_QUOTA_NEW },
  2653. { IDM_QUOTA_DELETE, IDS_TT_QUOTA_DELETE },
  2654. { IDM_QUOTA_PROPERTIES, IDS_TT_QUOTA_PROPERTIES },
  2655. { IDM_EDIT_UNDO, IDS_TT_EDIT_UNDO },
  2656. { IDM_EDIT_FIND, IDS_TT_EDIT_FIND }
  2657. };
  2658. INT idTT = -1;
  2659. for (INT i = 0; i < ARRAYSIZE(CmdTTXRef) && -1 == idTT; i++)
  2660. {
  2661. if (CmdTTXRef[i].idCmd == pToolTipText->hdr.idFrom)
  2662. idTT = CmdTTXRef[i].idTT;
  2663. }
  2664. if (-1 != idTT)
  2665. {
  2666. m_strDispText.Format(g_hInstDll, idTT);
  2667. pToolTipText->lpszText = (LPTSTR)m_strDispText;
  2668. }
  2669. return 0;
  2670. }
  2671. ///////////////////////////////////////////////////////////////////////////////
  2672. /* Function: DetailsView::LV_OnTTN_NeedText
  2673. Description: Handles requests for tooltip text for the listview tooltip
  2674. window. This is where we get the foldername DOMAIN/USERNAME text for when the
  2675. "domain name" column is hidden.
  2676. Arguments:
  2677. pToolTipText - Address of tooltip text notification information.
  2678. Returns: Always returns 0.
  2679. Revision History:
  2680. Date Description Programmer
  2681. -------- --------------------------------------------------- ----------
  2682. 09/09/96 Initial creation. BrianAu
  2683. 10/11/96 Added support for draggable columns. BrianAu
  2684. */
  2685. ///////////////////////////////////////////////////////////////////////////////
  2686. LRESULT
  2687. DetailsView::LV_OnTTN_NeedText(
  2688. TOOLTIPTEXT *pToolTipText
  2689. )
  2690. {
  2691. //
  2692. // Only provide text when the mouse is over the "User Name" column.
  2693. //
  2694. if (-1 != m_iLastItemHit)
  2695. {
  2696. INT cxMin = 0;
  2697. INT cxMax = 0;
  2698. INT cHdrs = Header_GetItemCount(m_hwndHeader);
  2699. for (INT i = 0; i < cHdrs; i++)
  2700. {
  2701. //
  2702. // Find the left and right X coordinate for the "Name" column.
  2703. //
  2704. INT iCol = Header_OrderToIndex(m_hwndHeader, i);
  2705. INT cxCol = ListView_GetColumnWidth(m_hwndListView, iCol);
  2706. if (DetailsView::idCol_Name == m_ColMap.SubItemToId(iCol))
  2707. {
  2708. cxMax = cxMin + cxCol;
  2709. break;
  2710. }
  2711. else
  2712. {
  2713. cxMin += cxCol;
  2714. }
  2715. }
  2716. //
  2717. // cxMin now contains left edge of Name column.
  2718. // cxMax now contains right edge of Name column.
  2719. //
  2720. if (m_ptMouse.x >= cxMin && m_ptMouse.x <= cxMax)
  2721. {
  2722. PDISKQUOTA_USER pUser = NULL;
  2723. if (m_UserList.Retrieve((LPVOID *)&pUser, m_iLastItemHit))
  2724. {
  2725. TCHAR szContainer[MAX_DOMAIN] = { TEXT('\0') };
  2726. TCHAR szLogonName[MAX_USERNAME] = { TEXT('\0') };
  2727. TCHAR szDisplayName[MAX_FULL_USERNAME] = { TEXT('\0') };
  2728. pUser->GetName(szContainer, ARRAYSIZE(szContainer),
  2729. szLogonName, ARRAYSIZE(szLogonName),
  2730. szDisplayName, ARRAYSIZE(szDisplayName));
  2731. if (TEXT('\0') != szContainer[0] && TEXT('\0') != szLogonName[0])
  2732. {
  2733. if (TEXT('\0') != szDisplayName[0])
  2734. m_strDispText.Format(g_hInstDll,
  2735. IDS_FMT_DISPLAY_LOGON_CONTAINER,
  2736. szDisplayName,
  2737. szLogonName,
  2738. szContainer);
  2739. else
  2740. m_strDispText.Format(g_hInstDll,
  2741. IDS_FMT_LOGON_CONTAINER,
  2742. szLogonName,
  2743. szContainer);
  2744. pToolTipText->lpszText = (LPTSTR)m_strDispText;
  2745. }
  2746. else
  2747. {
  2748. pToolTipText->lpszText = NULL;
  2749. pToolTipText->szText[0] = TEXT('\0');
  2750. }
  2751. }
  2752. }
  2753. }
  2754. return 0;
  2755. }
  2756. ///////////////////////////////////////////////////////////////////////////////
  2757. /* Function: DetailsView::OnLVN_OwnerDataFindItem
  2758. Description: Handles LVN_ODFINDITEM for the listview control.
  2759. Arguments:
  2760. pFindInfo - Address of NMLVFINDITEM structure associated with the
  2761. notification.
  2762. Returns: 0-based index of found item. -1 if not found.
  2763. Revision History:
  2764. Date Description Programmer
  2765. -------- --------------------------------------------------- ----------
  2766. 02/21/97 Initial creation. Ownerdraw listview. BrianAu
  2767. */
  2768. ///////////////////////////////////////////////////////////////////////////////
  2769. LRESULT
  2770. DetailsView::OnLVN_OwnerDataFindItem(
  2771. NMLVFINDITEM *pFindInfo
  2772. )
  2773. {
  2774. INT iItem = -1;
  2775. switch(pFindInfo->lvfi.flags)
  2776. {
  2777. case LVFI_PARAM:
  2778. {
  2779. LPVOID pvUser = NULL;
  2780. m_UserList.Lock();
  2781. INT cUsers = m_UserList.Count();
  2782. for (INT i = 0; i < cUsers; i++)
  2783. {
  2784. if (m_UserList.Retrieve(&pvUser, i) &&
  2785. pvUser == (LPVOID)pFindInfo->lvfi.lParam)
  2786. {
  2787. iItem = i;
  2788. break;
  2789. }
  2790. }
  2791. m_UserList.ReleaseLock();
  2792. break;
  2793. }
  2794. default:
  2795. //
  2796. // This app only uses lParam for locating items.
  2797. //
  2798. break;
  2799. }
  2800. return iItem;
  2801. }
  2802. ///////////////////////////////////////////////////////////////////////////////
  2803. /* Function: DetailsView::OnLVN_GetDispInfo
  2804. Description: Handles LVN_GETDISPINFO for the listview control.
  2805. Arguments:
  2806. pDispInfo - Address of LV_DISPINFO structure associated with the
  2807. notification.
  2808. Returns: Always 0.
  2809. Revision History:
  2810. Date Description Programmer
  2811. -------- --------------------------------------------------- ----------
  2812. 02/21/97 Initial creation. Ownerdraw listview. BrianAu
  2813. */
  2814. ///////////////////////////////////////////////////////////////////////////////
  2815. LRESULT
  2816. DetailsView::OnLVN_GetDispInfo(
  2817. LV_DISPINFO * pDispInfo
  2818. )
  2819. {
  2820. PDISKQUOTA_USER pUser = NULL;
  2821. m_UserList.Retrieve((LPVOID *)&pUser, pDispInfo->item.iItem);
  2822. if (NULL != pUser)
  2823. {
  2824. if (LVIF_TEXT & pDispInfo->item.mask)
  2825. OnLVN_GetDispInfo_Text(pDispInfo, pUser);
  2826. if ((LVIF_IMAGE & pDispInfo->item.mask) &&
  2827. (m_ColMap.SubItemToId(pDispInfo->item.iSubItem) == DetailsView::idCol_Status))
  2828. {
  2829. OnLVN_GetDispInfo_Image(pDispInfo, pUser);
  2830. }
  2831. }
  2832. return 0;
  2833. }
  2834. ///////////////////////////////////////////////////////////////////////////////
  2835. /* Function: DetailsView::OnLVN_GetDispInfo_Text
  2836. Description: Handles LVN_GETDISPINFO - LVIF_TEXT for the listview control.
  2837. Arguments:
  2838. pDispInfo - Address of LV_DISPINFO structure associated with the
  2839. notification.
  2840. pUser - Address of user object for listview item.
  2841. Returns:
  2842. Revision History:
  2843. Date Description Programmer
  2844. -------- --------------------------------------------------- ----------
  2845. 08/20/96 Initial creation. BrianAu
  2846. 09/22/96 Added user "full name" support. BrianAu
  2847. */
  2848. ///////////////////////////////////////////////////////////////////////////////
  2849. LRESULT
  2850. DetailsView::OnLVN_GetDispInfo_Text(
  2851. LV_DISPINFO *pDispInfo,
  2852. PDISKQUOTA_USER pUser
  2853. )
  2854. {
  2855. HRESULT hResult = NO_ERROR;
  2856. LONGLONG llValue;
  2857. NUMBERFMT NumFmt;
  2858. DBGASSERT((NULL != pDispInfo));
  2859. DBGASSERT((NULL != pUser));
  2860. NumFmt.NumDigits = 0;
  2861. m_strDispText.Empty();
  2862. switch(m_ColMap.SubItemToId(pDispInfo->item.iSubItem))
  2863. {
  2864. case DetailsView::idCol_Status:
  2865. DBGASSERT((NULL != pUser));
  2866. switch(GetUserQuotaState(pUser))
  2867. {
  2868. case iUSERSTATE_OK:
  2869. m_strDispText = m_strStatusOK;
  2870. break;
  2871. case iUSERSTATE_WARNING:
  2872. m_strDispText = m_strStatusWarning;
  2873. break;
  2874. default:
  2875. DBGASSERT((0));
  2876. //
  2877. // Fall through.
  2878. //
  2879. case iUSERSTATE_OVERLIMIT:
  2880. m_strDispText = m_strStatusOverlimit;
  2881. break;
  2882. }
  2883. break;
  2884. case DetailsView::idCol_Folder:
  2885. {
  2886. DWORD dwAccountStatus = 0;
  2887. DBGASSERT((NULL != pUser));
  2888. pUser->GetAccountStatus(&dwAccountStatus);
  2889. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
  2890. {
  2891. pUser->GetName(m_strDispText.GetBuffer(MAX_PATH),
  2892. MAX_PATH,
  2893. NULL,
  2894. 0,
  2895. NULL,
  2896. 0);
  2897. }
  2898. else
  2899. {
  2900. //
  2901. // Non-normal account status. Leave domain column
  2902. // blank. Account name column will contain status information.
  2903. //
  2904. }
  2905. break;
  2906. }
  2907. case DetailsView::idCol_Name:
  2908. {
  2909. DWORD dwAccountStatus = 0;
  2910. DBGASSERT((NULL != pUser));
  2911. pUser->GetAccountStatus(&dwAccountStatus);
  2912. switch(dwAccountStatus)
  2913. {
  2914. case DISKQUOTA_USER_ACCOUNT_RESOLVED:
  2915. pUser->GetName(NULL, 0,
  2916. NULL, 0,
  2917. m_strDispText.GetBuffer(MAX_USERNAME), MAX_USERNAME);
  2918. m_strDispText.ReleaseBuffer();
  2919. break;
  2920. case DISKQUOTA_USER_ACCOUNT_UNRESOLVED:
  2921. m_strDispText = m_strAccountUnresolved;
  2922. break;
  2923. case DISKQUOTA_USER_ACCOUNT_UNKNOWN:
  2924. m_strDispText = m_strAccountUnknown;
  2925. break;
  2926. case DISKQUOTA_USER_ACCOUNT_INVALID:
  2927. m_strDispText = m_strAccountInvalid;
  2928. break;
  2929. case DISKQUOTA_USER_ACCOUNT_DELETED:
  2930. m_strDispText = m_strAccountDeleted;
  2931. break;
  2932. case DISKQUOTA_USER_ACCOUNT_UNAVAILABLE:
  2933. m_strDispText = m_strAccountUnavailable;
  2934. break;
  2935. }
  2936. break;
  2937. }
  2938. case DetailsView::idCol_LogonName:
  2939. {
  2940. DBGASSERT((NULL != pUser));
  2941. DWORD dwAccountStatus = 0;
  2942. pUser->GetAccountStatus(&dwAccountStatus);
  2943. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
  2944. {
  2945. //
  2946. // If the account SID has been resolved to a name,
  2947. // display the name.
  2948. //
  2949. pUser->GetName(NULL, 0,
  2950. m_strDispText.GetBuffer(MAX_USERNAME), MAX_USERNAME,
  2951. NULL, 0);
  2952. m_strDispText.ReleaseBuffer();
  2953. }
  2954. else
  2955. {
  2956. //
  2957. // If the account SID has NOT been resolved to a name, display
  2958. // the SID as a string.
  2959. //
  2960. BYTE Sid[MAX_SID_LEN];
  2961. DWORD cchSidStr = MAX_PATH;
  2962. if (SUCCEEDED(pUser->GetSid(Sid, ARRAYSIZE(Sid))))
  2963. {
  2964. SidToString(Sid, m_strDispText.GetBuffer(cchSidStr), &cchSidStr);
  2965. m_strDispText.ReleaseBuffer();
  2966. }
  2967. }
  2968. break;
  2969. }
  2970. case DetailsView::idCol_AmtUsed:
  2971. pUser->GetQuotaUsed(&llValue);
  2972. XBytes::FormatByteCountForDisplay(llValue,
  2973. m_strDispText.GetBuffer(40), 40);
  2974. break;
  2975. case DetailsView::idCol_Limit:
  2976. pUser->GetQuotaLimit(&llValue);
  2977. if (NOLIMIT == llValue)
  2978. m_strDispText = m_strNoLimit;
  2979. else
  2980. XBytes::FormatByteCountForDisplay(llValue,
  2981. m_strDispText.GetBuffer(40), 40);
  2982. break;
  2983. case DetailsView::idCol_Threshold:
  2984. pUser->GetQuotaThreshold(&llValue);
  2985. if (NOLIMIT == llValue)
  2986. m_strDispText = m_strNoLimit;
  2987. else
  2988. XBytes::FormatByteCountForDisplay(llValue,
  2989. m_strDispText.GetBuffer(40), 40);
  2990. break;
  2991. case DetailsView::idCol_PctUsed:
  2992. {
  2993. DWORD dwPct = 0;
  2994. hResult = CalcPctQuotaUsed(pUser, &dwPct);
  2995. if (SUCCEEDED(hResult))
  2996. m_strDispText.Format(TEXT("%1!d!"), dwPct);
  2997. else
  2998. m_strDispText = m_strNotApplicable; // Not a number.
  2999. break;
  3000. }
  3001. default:
  3002. break;
  3003. }
  3004. pDispInfo->item.pszText = (LPTSTR)m_strDispText; // Used by all text callbacks.
  3005. return 0;
  3006. }
  3007. ///////////////////////////////////////////////////////////////////////////////
  3008. /* Function: DetailsView::OnLVN_GetDispInfo_Image
  3009. Description: Handles LVN_GETDISPINFO - LVIF_IMAGE for the listview control.
  3010. Arguments:
  3011. pDispInfo - Address of LV_DISPINFO structure associated with the
  3012. notification.
  3013. pUser - Address of user object for listview item.
  3014. Returns:
  3015. Revision History:
  3016. Date Description Programmer
  3017. -------- --------------------------------------------------- ----------
  3018. 08/20/96 Initial creation. BrianAu
  3019. 09/12/96 Added CheckMark icon. BrianAu
  3020. */
  3021. ///////////////////////////////////////////////////////////////////////////////
  3022. LRESULT
  3023. DetailsView::OnLVN_GetDispInfo_Image(
  3024. LV_DISPINFO *pDispInfo,
  3025. PDISKQUOTA_USER pUser
  3026. )
  3027. {
  3028. switch(GetUserQuotaState(pUser))
  3029. {
  3030. case iUSERSTATE_OK:
  3031. pDispInfo->item.iImage = iIMAGELIST_ICON_OK;
  3032. break;
  3033. case iUSERSTATE_WARNING:
  3034. pDispInfo->item.iImage = iIMAGELIST_ICON_WARNING;
  3035. break;
  3036. default:
  3037. DBGASSERT((0));
  3038. //
  3039. // Fall through.
  3040. //
  3041. case iUSERSTATE_OVERLIMIT:
  3042. pDispInfo->item.iImage = iIMAGELIST_ICON_LIMIT;
  3043. break;
  3044. }
  3045. return 0;
  3046. }
  3047. ///////////////////////////////////////////////////////////////////////////////
  3048. /* Function: DetailsView::GetUserQuotaState
  3049. Description: Determines which of 3 states the user's quota values place
  3050. the user in. This is mainly used to determine what icon to display
  3051. in the "Status" column. It is also used to determine what text
  3052. to display in the "Status" column in a drag-drop report.
  3053. Arguments:
  3054. pUser - Address of user object for listview item.
  3055. Returns:
  3056. Revision History:
  3057. Date Description Programmer
  3058. -------- --------------------------------------------------- ----------
  3059. 10/10/96 Initial creation. BrianAu
  3060. */
  3061. ///////////////////////////////////////////////////////////////////////////////
  3062. INT
  3063. DetailsView::GetUserQuotaState(
  3064. PDISKQUOTA_USER pUser
  3065. )
  3066. {
  3067. LONGLONG llUsed;
  3068. LONGLONG llLimit;
  3069. INT iState = iUSERSTATE_OK;
  3070. DBGASSERT((NULL != pUser));
  3071. pUser->GetQuotaUsed(&llUsed);
  3072. pUser->GetQuotaLimit(&llLimit);
  3073. if (NOLIMIT != llLimit && llUsed > llLimit)
  3074. {
  3075. iState = iUSERSTATE_OVERLIMIT;
  3076. }
  3077. else
  3078. {
  3079. LONGLONG llThreshold;
  3080. pUser->GetQuotaThreshold(&llThreshold);
  3081. if (NOLIMIT != llThreshold && llUsed > llThreshold)
  3082. iState = iUSERSTATE_WARNING;
  3083. }
  3084. return iState;
  3085. }
  3086. ///////////////////////////////////////////////////////////////////////////////
  3087. /* Function: DetailsView::OnLVN_ColumnClick
  3088. Description: Handles LVN_COLUMNCLICK list view notifications.
  3089. This is received when the user selects a column's label.
  3090. Arguments:
  3091. pNm - Address of listview notification message structure.
  3092. Returns:
  3093. Revision History:
  3094. Date Description Programmer
  3095. -------- --------------------------------------------------- ----------
  3096. 08/20/96 Initial creation. BrianAu
  3097. */
  3098. ///////////////////////////////////////////////////////////////////////////////
  3099. LRESULT
  3100. DetailsView::OnLVN_ColumnClick(
  3101. NM_LISTVIEW *pNm
  3102. )
  3103. {
  3104. INT idCol = m_ColMap.SubItemToId(pNm->iSubItem);
  3105. if (idCol != m_iLastColSorted)
  3106. {
  3107. //
  3108. // New column selected. Reset to ascending sort order.
  3109. //
  3110. m_fSortDirection = 0;
  3111. }
  3112. else
  3113. {
  3114. //
  3115. // Column selected more than once. Toggle sort order.
  3116. //
  3117. m_fSortDirection ^= 1;
  3118. }
  3119. SortObjects(idCol, m_fSortDirection);
  3120. //
  3121. // Remember what column was selected.
  3122. //
  3123. m_iLastColSorted = idCol;
  3124. return 0;
  3125. }
  3126. ///////////////////////////////////////////////////////////////////////////////
  3127. /* Function: DetailsView::OnLVN_ItemChanged
  3128. Description: Handles LVN_ITEMCHANGED listview notifications.
  3129. Updates the selected-item-count in the status bar.
  3130. Arguments:
  3131. pNm - Address of listview notification structure.
  3132. Returns:
  3133. Revision History:
  3134. Date Description Programmer
  3135. -------- --------------------------------------------------- ----------
  3136. 08/20/96 Initial creation. BrianAu
  3137. 05/18/97 Added promotion of user object in name resolution BrianAu
  3138. queue.
  3139. */
  3140. ///////////////////////////////////////////////////////////////////////////////
  3141. LRESULT
  3142. DetailsView::OnLVN_ItemChanged(
  3143. NM_LISTVIEW *pNm
  3144. )
  3145. {
  3146. if (LVIS_FOCUSED & pNm->uNewState)
  3147. {
  3148. if (!m_bMenuActive)
  3149. {
  3150. //
  3151. // Only update the item count if a menu item is not active.
  3152. // This method is called WHENEVER an item is updated. This includes
  3153. // asynchronous notifications following name resolution. Without
  3154. // this check, a menu's descriptive text can be overwritten while
  3155. // the user is walking through menu items.
  3156. //
  3157. ShowItemCountInStatusBar();
  3158. }
  3159. PDISKQUOTA_USER pUser = NULL;
  3160. m_UserList.Lock();
  3161. m_UserList.Retrieve((LPVOID *)&pUser, pNm->iItem);
  3162. if (NULL != pUser &&
  3163. NULL != m_pQuotaControl)
  3164. {
  3165. DWORD dwAccountStatus = 0;
  3166. pUser->GetAccountStatus(&dwAccountStatus);
  3167. if (DISKQUOTA_USER_ACCOUNT_UNRESOLVED == dwAccountStatus)
  3168. {
  3169. //
  3170. // If the user object hasn't been resolved yet, promote it to the
  3171. // head of the quota controller's SID-Name resolver queue.
  3172. // This will speed up the name resolution for this user without
  3173. // performing a blocking operation.
  3174. //
  3175. m_pQuotaControl->GiveUserNameResolutionPriority(pUser);
  3176. }
  3177. }
  3178. m_UserList.ReleaseLock();
  3179. }
  3180. else if ((0 == pNm->uNewState) || (LVIS_SELECTED & pNm->uNewState))
  3181. {
  3182. ShowItemCountInStatusBar();
  3183. }
  3184. return 0;
  3185. }
  3186. ///////////////////////////////////////////////////////////////////////////////
  3187. /* Function: DetailsView::SetFocus
  3188. Description: Called whenever the main window receives focus. Immediately
  3189. transfers focus to the listview control. The listview in turn
  3190. ensures that one or more items are highlighted.
  3191. Arguments: Std WndProc arguments.
  3192. Returns: Always returns 0.
  3193. Revision History:
  3194. Date Description Programmer
  3195. -------- --------------------------------------------------- ----------
  3196. 09/09/96 Initial creation. BrianAu
  3197. */
  3198. ///////////////////////////////////////////////////////////////////////////////
  3199. LRESULT
  3200. DetailsView::OnSetFocus(
  3201. HWND hWnd,
  3202. UINT message,
  3203. WPARAM wParam,
  3204. LPARAM lParam
  3205. )
  3206. {
  3207. if (NULL != m_hwndListView)
  3208. SetFocus(m_hwndListView);
  3209. return 0;
  3210. }
  3211. ///////////////////////////////////////////////////////////////////////////////
  3212. /* Function: DetailsView::OnSize
  3213. Description: Handles WM_SIZE message.
  3214. Arguments: Standard WndProc arguments.
  3215. Returns: Always returns 0.
  3216. Revision History:
  3217. Date Description Programmer
  3218. -------- --------------------------------------------------- ----------
  3219. 08/20/96 Initial creation. BrianAu
  3220. 05/20/97 Added positioning of "Find User" combo in toolbar. BrianAu
  3221. */
  3222. ///////////////////////////////////////////////////////////////////////////////
  3223. LRESULT
  3224. DetailsView::OnSize(
  3225. HWND hWnd,
  3226. UINT message,
  3227. WPARAM wParam,
  3228. LPARAM lParam
  3229. )
  3230. {
  3231. RECT rcMain;
  3232. RECT rcListView;
  3233. GetClientRect(hWnd, &rcMain); // How big's the main window?
  3234. rcListView = rcMain;
  3235. if (m_lvsi.fToolBar)
  3236. {
  3237. //
  3238. // Adjust toolbar if it's visible.
  3239. //
  3240. RECT rcToolBar;
  3241. INT cyToolBar = 0;
  3242. SendMessage(m_hwndToolBar, message, wParam, lParam);
  3243. GetClientRect(m_hwndToolBar, &rcToolBar);
  3244. cyToolBar = rcToolBar.bottom - rcToolBar.top;
  3245. rcListView.top += (cyToolBar + 1);
  3246. //
  3247. // Position the "Find User" combo box to the immediate right of the
  3248. // "Find" toolbar button.
  3249. // This code assumes that the "Find" toolbar button is the right-most
  3250. // button in the toolbar.
  3251. //
  3252. INT cButtons = (INT)SendMessage(m_hwndToolBar, TB_BUTTONCOUNT, 0, 0);
  3253. if (0 < cButtons)
  3254. {
  3255. RECT rcButton;
  3256. SendMessage(m_hwndToolBar, TB_GETITEMRECT, cButtons - 1, (LPARAM)&rcButton);
  3257. RECT rcCombo;
  3258. GetWindowRect(m_hwndToolbarCombo, &rcCombo);
  3259. SetWindowPos(m_hwndToolbarCombo,
  3260. NULL,
  3261. rcButton.right + 1,
  3262. rcButton.top + 1,
  3263. 0, 0,
  3264. SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
  3265. }
  3266. }
  3267. if (m_lvsi.fStatusBar)
  3268. {
  3269. //
  3270. // Adjust status bar if it's visible.
  3271. //
  3272. RECT rcStatusBar;
  3273. INT cyStatusBar = 0;
  3274. SendMessage(m_hwndStatusBar, message, wParam, lParam);
  3275. GetClientRect(m_hwndStatusBar, &rcStatusBar);
  3276. cyStatusBar = rcStatusBar.bottom - rcStatusBar.top;
  3277. rcListView.bottom -= cyStatusBar;
  3278. }
  3279. //
  3280. // Adjust the listview. Accounts for toolbar and status bar.
  3281. //
  3282. MoveWindow(m_hwndListView,
  3283. 0,
  3284. rcListView.top,
  3285. rcListView.right - rcListView.left,
  3286. rcListView.bottom - rcListView.top,
  3287. TRUE);
  3288. return 0;
  3289. }
  3290. ///////////////////////////////////////////////////////////////////////////////
  3291. /* Function: DetailsView::SelectAllItems
  3292. Description: Highlights all items in the listview for selection.
  3293. Arguments: None.
  3294. Returns:
  3295. Revision History:
  3296. Date Description Programmer
  3297. -------- --------------------------------------------------- ----------
  3298. 08/20/96 Initial creation. BrianAu
  3299. */
  3300. ///////////////////////////////////////////////////////////////////////////////
  3301. LRESULT
  3302. DetailsView::SelectAllItems(
  3303. VOID
  3304. )
  3305. {
  3306. INT cItems = ListView_GetItemCount(m_hwndListView);
  3307. DWORD dwState = 0;
  3308. CAutoWaitCursor waitcursor;
  3309. SetFocus(m_hwndListView);
  3310. //
  3311. // This isn't documented but it's the way the shell does it for DefView.
  3312. //
  3313. ListView_SetItemState(m_hwndListView, -1, LVIS_SELECTED, LVIS_SELECTED);
  3314. return 0;
  3315. }
  3316. ///////////////////////////////////////////////////////////////////////////////
  3317. /* Function: DetailsView::InvertSelection
  3318. Description: Selects all items that are not selected and unselects all
  3319. items that are.
  3320. Arguments: None.
  3321. Returns:
  3322. Revision History:
  3323. Date Description Programmer
  3324. -------- --------------------------------------------------- ----------
  3325. 08/20/96 Initial creation. BrianAu
  3326. */
  3327. ///////////////////////////////////////////////////////////////////////////////
  3328. LRESULT
  3329. DetailsView::InvertSelectedItems(
  3330. VOID
  3331. )
  3332. {
  3333. INT iItem = -1;
  3334. CAutoWaitCursor waitcursor;
  3335. SetFocus(m_hwndListView);
  3336. while ((iItem = ListView_GetNextItem(m_hwndListView, iItem, 0)) != -1)
  3337. {
  3338. DWORD dwState;
  3339. dwState = ListView_GetItemState(m_hwndListView, iItem, LVIS_SELECTED);
  3340. dwState ^= LVNI_SELECTED;
  3341. ListView_SetItemState(m_hwndListView, iItem, dwState, LVIS_SELECTED);
  3342. }
  3343. return 0;
  3344. }
  3345. ///////////////////////////////////////////////////////////////////////////////
  3346. /* Function: DetailsView::OnHelpAbout
  3347. Description: Handler for "About Windows NT" menu option.
  3348. Arguments:
  3349. hWnd - Handle of parent window for "about" dialog.
  3350. Returns:
  3351. Revision History:
  3352. Date Description Programmer
  3353. -------- --------------------------------------------------- ----------
  3354. 08/20/96 Initial creation. BrianAu
  3355. */
  3356. ///////////////////////////////////////////////////////////////////////////////
  3357. LRESULT
  3358. DetailsView::OnHelpAbout(
  3359. HWND hWnd
  3360. )
  3361. {
  3362. TCHAR szOpSysName[MAX_PATH];
  3363. LoadString(g_hInstDll, IDS_WINDOWS, szOpSysName, ARRAYSIZE(szOpSysName));
  3364. ShellAbout(hWnd, szOpSysName, NULL, NULL);
  3365. return 0;
  3366. }
  3367. ///////////////////////////////////////////////////////////////////////////////
  3368. /* Function: DetailsView::OnHelpTopics
  3369. Description: Handler for "Help Topics" menu option.
  3370. Arguments:
  3371. hWnd - Handle of parent window for the help UI.
  3372. Returns:
  3373. Revision History:
  3374. Date Description Programmer
  3375. -------- --------------------------------------------------- ----------
  3376. 08/20/96 Initial creation. BrianAu
  3377. */
  3378. ///////////////////////////////////////////////////////////////////////////////
  3379. LRESULT
  3380. DetailsView::OnHelpTopics(
  3381. HWND hWnd
  3382. )
  3383. {
  3384. const char szHtmlHelpFileA[] = "DSKQUOUI.CHM > windefault";
  3385. const char szHtmlHelpTopicA[] = "sag_DQtopnode.htm";
  3386. HtmlHelpA(hWnd,
  3387. szHtmlHelpFileA,
  3388. HH_DISPLAY_TOPIC,
  3389. (DWORD_PTR)szHtmlHelpTopicA);
  3390. return 0;
  3391. }
  3392. bool
  3393. DetailsView::SingleSelectionIsAdmin(
  3394. void
  3395. )
  3396. {
  3397. bool bResult = false;
  3398. if (1 == ListView_GetSelectedCount(m_hwndListView))
  3399. {
  3400. INT iItem = ListView_GetNextItem(m_hwndListView, -1, LVNI_SELECTED);
  3401. if (-1 != iItem)
  3402. {
  3403. PDISKQUOTA_USER pUser = NULL;
  3404. if (m_UserList.Retrieve((LPVOID *)&pUser, iItem) && NULL != pUser)
  3405. {
  3406. bResult = !!UserIsAdministrator(pUser);
  3407. }
  3408. }
  3409. }
  3410. return bResult;
  3411. }
  3412. //
  3413. // NTRAID#NTBUG9-157269-2000/08/08-BrianAu Temporary workaround.
  3414. //
  3415. // This workaround was added for bug 157269. However,
  3416. // the root cause is bug 24904. Once 24904 is fixed, we should
  3417. // be able to remove this code. Bug 157269 provides a detailed
  3418. // explanation of the issue.
  3419. //
  3420. // Since it's a temporary function and doesn't use any members
  3421. // of the DetailsView class I chose to not make it a member
  3422. // of DetailsView. The logic used is taken from
  3423. // DiskQuotaControl::AddUserSid.
  3424. //
  3425. // The Retry and sleep code is a total hack.
  3426. //
  3427. bool
  3428. UserWasReallyDeleted(
  3429. IDiskQuotaControl *pQuotaControl,
  3430. IDiskQuotaUser *pIUser
  3431. )
  3432. {
  3433. bool bDeleted = true;
  3434. BYTE rgbSid[MAX_SID_LEN];
  3435. HRESULT hr = pIUser->GetSid(rgbSid, sizeof(rgbSid));
  3436. if (SUCCEEDED(hr))
  3437. {
  3438. IDiskQuotaUser *pIUserTemp;
  3439. hr = pQuotaControl->FindUserSid(rgbSid,
  3440. DISKQUOTA_USERNAME_RESOLVE_NONE,
  3441. &pIUserTemp);
  3442. if (SUCCEEDED(hr))
  3443. {
  3444. const int MAX_RETRY_COUNT = 1;
  3445. const DWORD RETRY_WAIT_MS = 100;
  3446. for (int i = 0; i <= MAX_RETRY_COUNT && bDeleted; i++)
  3447. {
  3448. LONGLONG llLimit;
  3449. LONGLONG llThreshold;
  3450. LONGLONG llUsed;
  3451. pIUserTemp->GetQuotaLimit(&llLimit);
  3452. pIUserTemp->GetQuotaThreshold(&llThreshold);
  3453. pIUserTemp->GetQuotaUsed(&llUsed);
  3454. bDeleted = ((MARK4DEL == llLimit) ||
  3455. ( 0 == llLimit &&
  3456. 0 == llThreshold &&
  3457. 0 == llUsed));
  3458. if (bDeleted && i < MAX_RETRY_COUNT)
  3459. {
  3460. Sleep(RETRY_WAIT_MS);
  3461. //
  3462. // Invalidate cached data so next request fetches from
  3463. // volume, not memory.
  3464. //
  3465. pIUserTemp->Invalidate();
  3466. }
  3467. }
  3468. pIUserTemp->Release();
  3469. }
  3470. }
  3471. return bDeleted;
  3472. }
  3473. ///////////////////////////////////////////////////////////////////////////////
  3474. /* Function: DetailsView::OnCmdDelete
  3475. Description: Called whenever the user presses DEL or selects the "Delete"
  3476. option from the main menu, context menu or toolbar.
  3477. The method attempts to delete the selected records. Any records that
  3478. have 1+ bytes charged to them will not be deleted. A message box
  3479. is displayed informing the user if any selected records have 1+ bytes.
  3480. Arguments: None.
  3481. Returns: Nothing.
  3482. Exceptions: OutOfMemory.
  3483. Revision History:
  3484. Date Description Programmer
  3485. -------- --------------------------------------------------- ----------
  3486. 09/11/96 Initial creation. BrianAu
  3487. 03/11/98 Added code for resolving "owned" files. BrianAu
  3488. */
  3489. ///////////////////////////////////////////////////////////////////////////////
  3490. LRESULT
  3491. DetailsView::OnCmdDelete(
  3492. VOID
  3493. )
  3494. {
  3495. HRESULT hResult = NO_ERROR;
  3496. //
  3497. // Make sure they really want to do this.
  3498. // Early return if they don't.
  3499. // Don't ask if it's a single selection and the selected user
  3500. // is BUILTIN/Administrators. This user can't be deleted anyway so
  3501. // we don't want to ask for confirmation. The deletion attempt will
  3502. // fail later and we'll display a "can't be deleted" msgbox.
  3503. //
  3504. if (!SingleSelectionIsAdmin() && IDNO == DiskQuotaMsgBox(m_hwndListView,
  3505. IDS_CONFIRM_DELETE_USER,
  3506. IDS_TITLE_DISK_QUOTA,
  3507. MB_ICONWARNING | MB_YESNO))
  3508. {
  3509. return 0;
  3510. }
  3511. //
  3512. // Clear any previous undo actions.
  3513. // Only allow undo for a single delete (single or multi-user) operation.
  3514. //
  3515. m_pUndoList->Clear();
  3516. CAutoWndEnable autoenable(m_hwndListView);
  3517. PDISKQUOTA_USER pUser = NULL;
  3518. INT iItem = -1;
  3519. INT cItemsToDelete = ListView_GetSelectedCount(m_hwndListView);
  3520. ProgressDialog dlgProgress(IDD_PROGRESS,
  3521. IDC_PROGRESS_BAR,
  3522. IDC_TXT_PROGRESS_DESCRIPTION,
  3523. IDC_TXT_PROGRESS_FILENAME);
  3524. if (2 < cItemsToDelete)
  3525. {
  3526. //
  3527. // Create and display a progress dialog if we're deleting more than 2
  3528. // user quota records.
  3529. //
  3530. if (dlgProgress.Create(g_hInstDll,
  3531. m_hwndMain))
  3532. {
  3533. autoenable.Enable(false);
  3534. dlgProgress.ProgressBarInit(0, cItemsToDelete, 1);
  3535. dlgProgress.SetDescription(MAKEINTRESOURCE(IDS_PROGRESS_DELETING));
  3536. dlgProgress.Show();
  3537. }
  3538. }
  3539. //
  3540. // Set each user's threshold and limit to -2 (MARK4DEL) and remove the
  3541. // item from the listview.
  3542. // A limit of -2 tells the quota system (NTFS) that the record should be
  3543. // removed from the quota file. However, if the user still has quota
  3544. // charged, the record will be restored.
  3545. //
  3546. CAutoSetRedraw autoredraw(m_hwndListView, false);
  3547. CArray<IDiskQuotaUser *> rgpUsersWithFiles;
  3548. LONGLONG Threshold;
  3549. LONGLONG Limit;
  3550. while(-1 != (iItem = ListView_GetNextItem(m_hwndListView, iItem, LVNI_SELECTED)) &&
  3551. !dlgProgress.UserCancelled())
  3552. {
  3553. if (m_UserList.Retrieve((LPVOID *)&pUser, iItem))
  3554. {
  3555. if (UserIsAdministrator(pUser))
  3556. {
  3557. //
  3558. // Deletion of the BUILTINS\Administrators quota record is not
  3559. // allowed.
  3560. //
  3561. CString strText(g_hInstDll, IDS_CANT_DELETE_ADMIN_RECORD);
  3562. DiskQuotaMsgBox(dlgProgress.m_hWnd ? dlgProgress.m_hWnd : m_hwndListView,
  3563. strText,
  3564. IDS_TITLE_DISK_QUOTA,
  3565. MB_ICONWARNING | MB_OK);
  3566. }
  3567. else
  3568. {
  3569. //
  3570. // Get threshold and limit values for undo action.
  3571. //
  3572. pUser->GetQuotaThreshold(&Threshold);
  3573. pUser->GetQuotaLimit(&Limit);
  3574. //
  3575. // Remove the user from the quota file.
  3576. //
  3577. hResult = m_pQuotaControl->DeleteUser(pUser);
  3578. if (SUCCEEDED(hResult))
  3579. {
  3580. if (!UserWasReallyDeleted(m_pQuotaControl, pUser))
  3581. {
  3582. //
  3583. // NTRAID#NTBUG9-157269-2000/08/08-BrianAu Temporary workaround.
  3584. //
  3585. // This workaround was added for bug 157269. However,
  3586. // the root cause is bug 24904. Once 24904 is fixed, we should
  3587. // be able to remove this code. Bug 157269 provides a detailed
  3588. // explanation of the issue.
  3589. //
  3590. pUser->SetQuotaThreshold(Threshold, TRUE);
  3591. pUser->SetQuotaLimit(Limit, TRUE);
  3592. }
  3593. else
  3594. {
  3595. pUser->AddRef(); // Giving ptr to UNDO list.
  3596. try
  3597. {
  3598. autoptr<UndoDelete> ptrUndoDelete = new UndoDelete(pUser, Threshold, Limit);
  3599. m_pUndoList->Add(ptrUndoDelete);
  3600. ptrUndoDelete.disown();
  3601. }
  3602. catch(CAllocException& e)
  3603. {
  3604. pUser->Release(); // Release from Undo list.
  3605. EnableWindow(m_hwndMain, TRUE);
  3606. throw;
  3607. }
  3608. ListView_DeleteItem(m_hwndListView, iItem);
  3609. //
  3610. // Deletion is successful. Now actually remove the user from
  3611. // the user list.
  3612. //
  3613. m_UserList.Remove((LPVOID *)&pUser, iItem);
  3614. pUser->Release(); // Release from listview.
  3615. //
  3616. // Decrement the search index by 1 since what was index + 1
  3617. // is now index. ListView_GetNextItem ignores the item at "index".
  3618. //
  3619. iItem--;
  3620. }
  3621. dlgProgress.ProgressBarAdvance();
  3622. }
  3623. else if (ERROR_FILE_EXISTS == HRESULT_CODE(hResult))
  3624. {
  3625. //
  3626. // One more we couldn't delete.
  3627. //
  3628. rgpUsersWithFiles.Append(pUser);
  3629. }
  3630. }
  3631. }
  3632. }
  3633. if (0 < rgpUsersWithFiles.Count())
  3634. {
  3635. //
  3636. // Display a dialog listing users selected for deletion and
  3637. // and the files owned by those users on this volume. From the dialog,
  3638. // the admin can Delete, Move or Take Ownership of the files.
  3639. //
  3640. dlgProgress.SetDescription(MAKEINTRESOURCE(IDS_PROGRESS_SEARCHINGFORFILES));
  3641. CFileOwnerDialog dlg(g_hInstDll,
  3642. dlgProgress.m_hWnd ? dlgProgress.m_hWnd : m_hwndListView,
  3643. m_idVolume.FSPath(),
  3644. rgpUsersWithFiles);
  3645. dlg.Run();
  3646. dlgProgress.SetDescription(MAKEINTRESOURCE(IDS_PROGRESS_DELETING));
  3647. int cUsers = rgpUsersWithFiles.Count();
  3648. int cCannotDelete = 0;
  3649. for (int i = 0; i < cUsers; i++)
  3650. {
  3651. pUser = rgpUsersWithFiles[i];
  3652. //
  3653. // Get threshold and limit values for undo action.
  3654. //
  3655. pUser->GetQuotaThreshold(&Threshold);
  3656. pUser->GetQuotaLimit(&Limit);
  3657. //
  3658. // Try to remove the user from the quota file.
  3659. //
  3660. hResult = m_pQuotaControl->DeleteUser(pUser);
  3661. if (SUCCEEDED(hResult))
  3662. {
  3663. if (!UserWasReallyDeleted(m_pQuotaControl, pUser))
  3664. {
  3665. //
  3666. // NTRAID#NTBUG9-157269-2000/08/08-BrianAu Temporary workaround.
  3667. //
  3668. // This workaround was added for bug 157269. However,
  3669. // the root cause is bug 24904. Once 24904 is fixed, we should
  3670. // be able to remove this code. Bug 157269 provides a detailed
  3671. // explanation of the issue.
  3672. //
  3673. pUser->SetQuotaThreshold(Threshold, TRUE);
  3674. pUser->SetQuotaLimit(Limit, TRUE);
  3675. }
  3676. else
  3677. {
  3678. pUser->AddRef(); // Giving ptr to UNDO list.
  3679. try
  3680. {
  3681. autoptr<UndoDelete> ptrUndoDelete = new UndoDelete(pUser, Threshold, Limit);
  3682. m_pUndoList->Add(ptrUndoDelete);
  3683. ptrUndoDelete.disown();
  3684. }
  3685. catch(CAllocException& e)
  3686. {
  3687. pUser->Release(); // Release from Undo list.
  3688. throw;
  3689. }
  3690. iItem = FindUserByObjPtr(pUser);
  3691. if (-1 != iItem)
  3692. {
  3693. ListView_DeleteItem(m_hwndListView, iItem);
  3694. //
  3695. // Deletion is successful. Now actually remove the user from
  3696. // the user list.
  3697. //
  3698. m_UserList.Remove((LPVOID *)&pUser, iItem);
  3699. pUser->Release(); // Release from listview.
  3700. }
  3701. }
  3702. dlgProgress.ProgressBarAdvance();
  3703. }
  3704. else if (ERROR_FILE_EXISTS == HRESULT_CODE(hResult))
  3705. {
  3706. cCannotDelete++;
  3707. }
  3708. }
  3709. if (0 < cCannotDelete)
  3710. {
  3711. //
  3712. // One or more records could not be deleted because they have
  3713. // disk space charged to them.
  3714. //
  3715. CString strText;
  3716. if (1 == cCannotDelete)
  3717. strText.Format(g_hInstDll, IDS_CANNOT_DELETE_USER);
  3718. else
  3719. strText.Format(g_hInstDll, IDS_CANNOT_DELETE_USERS, cCannotDelete);
  3720. DiskQuotaMsgBox(m_hwndListView,
  3721. strText,
  3722. IDS_TITLE_DISK_QUOTA,
  3723. MB_ICONINFORMATION | MB_OK);
  3724. }
  3725. }
  3726. ShowItemCountInStatusBar();
  3727. if (FAILED(hResult) && ERROR_FILE_EXISTS != HRESULT_CODE(hResult))
  3728. {
  3729. //
  3730. // Something bad happened.
  3731. // FEATURE: Do we need to discriminate between a general failure and
  3732. // a quota file write error?
  3733. //
  3734. DiskQuotaMsgBox(m_hwndListView,
  3735. IDS_ERROR_DELETE_USER,
  3736. IDS_TITLE_DISK_QUOTA,
  3737. MB_ICONERROR | MB_OK);
  3738. }
  3739. return 0;
  3740. }
  3741. ///////////////////////////////////////////////////////////////////////////////
  3742. /* Function: DetailsView::OnCmdUndo
  3743. Description: Called whenever the user presses Ctrl + Z or selects
  3744. the "Undo" option from the main menu, context menu or toolbar.
  3745. The method invokes the current undo list to "undo" its actions.
  3746. Arguments: None.
  3747. Returns: Always 0.
  3748. Revision History:
  3749. Date Description Programmer
  3750. -------- --------------------------------------------------- ----------
  3751. 10/01/96 Initial creation. BrianAu
  3752. 02/26/97 Added call to update status bar. BrianAu
  3753. */
  3754. ///////////////////////////////////////////////////////////////////////////////
  3755. LRESULT
  3756. DetailsView::OnCmdUndo(
  3757. VOID
  3758. )
  3759. {
  3760. if (NULL != m_pUndoList)
  3761. {
  3762. CAutoWaitCursor waitcursor;
  3763. m_pUndoList->Undo();
  3764. ShowItemCountInStatusBar();
  3765. }
  3766. return 0;
  3767. }
  3768. ///////////////////////////////////////////////////////////////////////////////
  3769. /* Function: DetailsView::OnCmdFind
  3770. Description: Called whenever the user presses Ctrl + F or selects
  3771. the "Find" option from the main menu, context menu or toolbar.
  3772. The method invokes the "Find User" dialog.
  3773. Arguments: None.
  3774. Returns: Always 0.
  3775. Revision History:
  3776. Date Description Programmer
  3777. -------- --------------------------------------------------- ----------
  3778. 05/20/97 Initial creation. BrianAu
  3779. */
  3780. ///////////////////////////////////////////////////////////////////////////////
  3781. LRESULT
  3782. DetailsView::OnCmdFind(
  3783. VOID
  3784. )
  3785. {
  3786. if (NULL != m_pUserFinder)
  3787. {
  3788. m_pUserFinder->InvokeFindDialog(m_hwndListView);
  3789. }
  3790. return 0;
  3791. }
  3792. ///////////////////////////////////////////////////////////////////////////////
  3793. /* Function: DetailsView::OnCmdProperties
  3794. Description: Displays a properties dialog for one or more selected objects.
  3795. Invoked when the user selects a "Properties" menu option, dbl clicks
  3796. a selection or presses Return for a selection.
  3797. Arguments: None.
  3798. Returns: Always returns 0.
  3799. Exceptions: OutOfMemory.
  3800. Revision History:
  3801. Date Description Programmer
  3802. -------- --------------------------------------------------- ----------
  3803. 08/20/96 Initial creation. BrianAu
  3804. 09/10/96 Added passing of LVSelection to prop sheet ctor. BrianAu
  3805. */
  3806. ///////////////////////////////////////////////////////////////////////////////
  3807. LRESULT
  3808. DetailsView::OnCmdProperties(
  3809. VOID
  3810. )
  3811. {
  3812. LVSelection lvs(m_hwndListView);
  3813. INT iItem = -1;
  3814. //
  3815. // Fill in arrays of user pointers and item indices.
  3816. //
  3817. while(-1 != (iItem = ListView_GetNextItem(m_hwndListView, iItem, LVNI_SELECTED)))
  3818. {
  3819. LPVOID pvUser = 0;
  3820. if (m_UserList.Retrieve(&pvUser, iItem))
  3821. {
  3822. //
  3823. // Add user object pointer and item index to the selection object.
  3824. // We'll use this container to communicate the selected items to the
  3825. // property sheet object.
  3826. // This can throw OutOfMemory.
  3827. //
  3828. lvs.Add((PDISKQUOTA_USER)pvUser, iItem);
  3829. }
  3830. }
  3831. if (0 < lvs.Count())
  3832. {
  3833. //
  3834. // Create and run the property sheet. It's modal.
  3835. // There's a condition where the user can select in the listview and
  3836. // nothing is actually selected (i.e. select below the last item).
  3837. // Therefore, we need the (0 < count) check.
  3838. //
  3839. m_pQuotaControl->AddRef();
  3840. UserPropSheet ups(m_pQuotaControl,
  3841. m_idVolume,
  3842. m_hwndListView,
  3843. lvs,
  3844. *m_pUndoList);
  3845. ups.Run();
  3846. }
  3847. return 0;
  3848. }
  3849. ///////////////////////////////////////////////////////////////////////////////
  3850. /* Function: DetailsView::OnCmdNew
  3851. Description: Displays a properties dialog for adding a new user to the
  3852. quota information file.
  3853. Invoked when the user selects the "New" menu option.
  3854. Arguments: None.
  3855. Returns: Always returns 0.
  3856. Exceptions: OutOfMemory.
  3857. Revision History:
  3858. Date Description Programmer
  3859. -------- --------------------------------------------------- ----------
  3860. 09/27/96 Initial creation. BrianAu
  3861. */
  3862. ///////////////////////////////////////////////////////////////////////////////
  3863. LRESULT
  3864. DetailsView::OnCmdNew(
  3865. VOID
  3866. )
  3867. {
  3868. //
  3869. // Create and run the AddUser dialog.
  3870. // Note that it first launches the DS Object Picker dialog.
  3871. //
  3872. m_pQuotaControl->AddRef();
  3873. AddUserDialog dlg(m_pQuotaControl,
  3874. m_idVolume,
  3875. g_hInstDll,
  3876. m_hwndListView,
  3877. m_hwndListView,
  3878. *m_pUndoList);
  3879. dlg.Run();
  3880. return 0;
  3881. }
  3882. ///////////////////////////////////////////////////////////////////////////////
  3883. /* Function: DetailsView::CreateVolumeDisplayName [static]
  3884. Description: Obtains the display name used by the shell for a given
  3885. volume.
  3886. Arguments:
  3887. pszDrive - Address of string containing drive name (i.e. "C:\").
  3888. pstrDisplayName - Address of CString object to receive the
  3889. display name.
  3890. Returns:
  3891. Revision History:
  3892. Date Description Programmer
  3893. -------- --------------------------------------------------- ----------
  3894. 06/30/97 Initial creation. BrianAu
  3895. */
  3896. ///////////////////////////////////////////////////////////////////////////////
  3897. HRESULT
  3898. DetailsView::CreateVolumeDisplayName(
  3899. const CVolumeID& idVolume, // [in] - "C:\" or "\\?\Volume{ <guid }\"
  3900. CString *pstrDisplayName // [out] - "My Disk (C:)"
  3901. )
  3902. {
  3903. HRESULT hr = E_FAIL;
  3904. if (idVolume.IsMountedVolume())
  3905. {
  3906. //
  3907. // If it's a mounted volume thingy "\\?\Volume{ <guid> }\", the shell won't
  3908. // understand it. Just use the default display name provided by the
  3909. // CVolumeID object.
  3910. //
  3911. *pstrDisplayName = idVolume.ForDisplay();
  3912. }
  3913. else
  3914. {
  3915. //
  3916. // It's a normal volume. Get the display name the shell uses.
  3917. //
  3918. com_autoptr<IShellFolder> ptrDesktop;
  3919. //
  3920. // Bind to the desktop folder.
  3921. //
  3922. hr = SHGetDesktopFolder(ptrDesktop.getaddr());
  3923. if (SUCCEEDED(hr))
  3924. {
  3925. sh_autoptr<ITEMIDLIST> ptrIdlDrives;
  3926. hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, ptrIdlDrives.getaddr());
  3927. if (SUCCEEDED(hr))
  3928. {
  3929. //
  3930. // Bind to the "Drives" folder.
  3931. //
  3932. com_autoptr<IShellFolder> ptrDrives;
  3933. hr = ptrDesktop->BindToObject(ptrIdlDrives, NULL, IID_IShellFolder, (LPVOID *)ptrDrives.getaddr());
  3934. if (SUCCEEDED(hr))
  3935. {
  3936. com_autoptr<IEnumIDList> ptrEnum;
  3937. //
  3938. // Enumerate all of the non-folder objects in the drives folder.
  3939. //
  3940. hr = ptrDrives->EnumObjects(NULL, SHCONTF_NONFOLDERS, ptrEnum.getaddr());
  3941. if (SUCCEEDED(hr))
  3942. {
  3943. sh_autoptr<ITEMIDLIST> ptrIdlItem;
  3944. ULONG ulFetched = 0;
  3945. LPCTSTR pszDrive = idVolume.ForParsing();
  3946. //
  3947. // For each item in the drives folder...
  3948. //
  3949. while(S_OK == ptrEnum->Next(1, ptrIdlItem.getaddr(), &ulFetched))
  3950. {
  3951. STRRET strretName;
  3952. //
  3953. // Get the non-display name form; i.e. "G:\"
  3954. //
  3955. hr = ptrDrives->GetDisplayNameOf(ptrIdlItem, SHGDN_FORPARSING, &strretName);
  3956. if (SUCCEEDED(hr))
  3957. {
  3958. TCHAR szName[MAX_PATH];
  3959. StrRetToBuf(&strretName, ptrIdlItem, szName, ARRAYSIZE(szName));
  3960. if (TEXT(':') == szName[1] &&
  3961. *pszDrive == szName[0])
  3962. {
  3963. //
  3964. // Get the display name form; i.e. "My Disk (G:)"
  3965. //
  3966. hr = ptrDrives->GetDisplayNameOf(ptrIdlItem, SHGDN_NORMAL, &strretName);
  3967. if (SUCCEEDED(hr))
  3968. {
  3969. StrRetToBuf(&strretName, ptrIdlItem, pstrDisplayName->GetBuffer(MAX_PATH), MAX_PATH);
  3970. pstrDisplayName->ReleaseBuffer();
  3971. }
  3972. break;
  3973. }
  3974. }
  3975. }
  3976. }
  3977. }
  3978. }
  3979. }
  3980. }
  3981. return hr;
  3982. }
  3983. ///////////////////////////////////////////////////////////////////////////////
  3984. /* Function: DetailsView::OnCmdImport
  3985. Description: Called when the user selects the "Import" option on the
  3986. "Quota" menu. Presents the "Open File" common dialog to get the
  3987. name for the file containing the import information. Then passes
  3988. the path off to an Importer object to do the actual import.
  3989. Arguments: None.
  3990. Returns:
  3991. Revision History:
  3992. Date Description Programmer
  3993. -------- --------------------------------------------------- ----------
  3994. 05/20/97 Initial creation. BrianAu
  3995. */
  3996. ///////////////////////////////////////////////////////////////////////////////
  3997. LRESULT
  3998. DetailsView::OnCmdImport(
  3999. VOID
  4000. )
  4001. {
  4002. HRESULT hResult = NO_ERROR;
  4003. TCHAR szFileName[MAX_PATH] = { TEXT('\0') };
  4004. TCHAR szTitle[80] = { TEXT('\0') };
  4005. LoadString(g_hInstDll, IDS_DLGTITLE_IMPORT, szTitle, ARRAYSIZE(szTitle));
  4006. OPENFILENAME ofn;
  4007. ZeroMemory(&ofn, sizeof(ofn));
  4008. ofn.lStructSize = sizeof(ofn);
  4009. ofn.hwndOwner = m_hwndMain;
  4010. ofn.hInstance = g_hInstDll;
  4011. ofn.lpstrFile = szFileName;
  4012. ofn.lpstrTitle = szTitle;
  4013. ofn.nMaxFile = ARRAYSIZE(szFileName);
  4014. ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR |
  4015. OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
  4016. //
  4017. // Get name of import file from user and import the files.
  4018. //
  4019. if (GetOpenFileName(&ofn))
  4020. {
  4021. Importer importer(*this);
  4022. hResult = importer.Import(ofn.lpstrFile);
  4023. }
  4024. return SUCCEEDED(hResult);
  4025. }
  4026. ///////////////////////////////////////////////////////////////////////////////
  4027. /* Function: DetailsView::OnCmdExport
  4028. Description: Called when the user selects the "Export" option on the
  4029. "Quota" menu or listview context menu. Presents the "Save File"
  4030. common dialog to get the name for the output file. It then creates
  4031. the doc file, the stream within the doc file and then calls the
  4032. DetailsView's IDataObject implementation to render the data on the
  4033. stream.
  4034. Arguments: None.
  4035. Returns:
  4036. Revision History:
  4037. Date Description Programmer
  4038. -------- --------------------------------------------------- ----------
  4039. 05/20/97 Initial creation. BrianAu
  4040. */
  4041. ///////////////////////////////////////////////////////////////////////////////
  4042. LRESULT
  4043. DetailsView::OnCmdExport(
  4044. VOID
  4045. )
  4046. {
  4047. HRESULT hResult = NO_ERROR;
  4048. IDataObject *pIDataObject = NULL;
  4049. try
  4050. {
  4051. hResult = QueryInterface(IID_IDataObject, (LPVOID *)&pIDataObject);
  4052. if (SUCCEEDED(hResult))
  4053. {
  4054. FORMATETC fmt;
  4055. DataObject::SetFormatEtc(fmt,
  4056. DataObject::m_CF_NtDiskQuotaExport,
  4057. TYMED_ISTREAM);
  4058. hResult = pIDataObject->QueryGetData(&fmt);
  4059. if (SUCCEEDED(hResult))
  4060. {
  4061. TCHAR szFileName[MAX_PATH] = { TEXT('\0') };
  4062. TCHAR szTitle[80] = { TEXT('\0') };
  4063. LoadString(g_hInstDll, IDS_DLGTITLE_EXPORT, szTitle, ARRAYSIZE(szTitle));
  4064. OPENFILENAME ofn;
  4065. ZeroMemory(&ofn, sizeof(ofn));
  4066. ofn.lStructSize = sizeof(ofn);
  4067. ofn.hwndOwner = m_hwndMain;
  4068. ofn.hInstance = g_hInstDll;
  4069. ofn.lpstrFile = szFileName;
  4070. ofn.lpstrTitle = szTitle;
  4071. ofn.nMaxFile = ARRAYSIZE(szFileName);
  4072. ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR |
  4073. OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
  4074. //
  4075. // Get output file name from user.
  4076. //
  4077. if (GetSaveFileName(&ofn))
  4078. {
  4079. DWORD grfMode = STGM_DIRECT | STGM_READWRITE |
  4080. STGM_CREATE | STGM_SHARE_EXCLUSIVE;
  4081. IStorage *pStg;
  4082. //
  4083. // Create the output doc file.
  4084. //
  4085. hResult = StgCreateDocfile(ofn.lpstrFile,
  4086. grfMode,
  4087. 0,
  4088. &pStg);
  4089. if (SUCCEEDED(hResult))
  4090. {
  4091. //
  4092. // Create the stream in the doc file.
  4093. //
  4094. IStream *pStm;
  4095. hResult = pStg->CreateStream(DataObject::SZ_EXPORT_STREAM_NAME,
  4096. grfMode,
  4097. 0, 0,
  4098. &pStm);
  4099. if (SUCCEEDED(hResult))
  4100. {
  4101. CStgMedium medium;
  4102. //
  4103. // Render the quota information onto the file stream.
  4104. //
  4105. hResult = pIDataObject->GetData(&fmt, &medium);
  4106. if (SUCCEEDED(hResult))
  4107. {
  4108. const LARGE_INTEGER ofsBegin = {0, 0};
  4109. ULARGE_INTEGER ofsOriginal = {0, 0};
  4110. hResult = medium.pstm->Seek(ofsBegin, STREAM_SEEK_CUR, &ofsOriginal);
  4111. if (SUCCEEDED(hResult))
  4112. {
  4113. hResult = medium.pstm->Seek(ofsBegin, STREAM_SEEK_SET, NULL);
  4114. if (SUCCEEDED(hResult))
  4115. {
  4116. ULARGE_INTEGER cb = {0xFFFFFFFF, 0xFFFFFFFF};
  4117. medium.pstm->CopyTo(pStm, cb, NULL, NULL);
  4118. const LARGE_INTEGER ofs = { ofsOriginal.LowPart, (LONG)ofsOriginal.HighPart };
  4119. medium.pstm->Seek(ofs, STREAM_SEEK_SET, NULL);
  4120. }
  4121. }
  4122. }
  4123. pStm->Release();
  4124. }
  4125. pStg->Release();
  4126. }
  4127. if (FAILED(hResult))
  4128. {
  4129. UINT iMsg = IDS_EXPORT_STREAM_FAILED;
  4130. switch(hResult)
  4131. {
  4132. case STG_E_ACCESSDENIED:
  4133. iMsg = IDS_EXPORT_STREAM_NOACCESS;
  4134. break;
  4135. case E_OUTOFMEMORY:
  4136. case STG_E_INSUFFICIENTMEMORY:
  4137. iMsg = IDS_EXPORT_STREAM_OUTOFMEMORY;
  4138. break;
  4139. case STG_E_INVALIDNAME:
  4140. iMsg = IDS_EXPORT_STREAM_INVALIDNAME;
  4141. break;
  4142. case STG_E_TOOMANYOPENFILES:
  4143. iMsg = IDS_EXPORT_STREAM_TOOMANYFILES;
  4144. break;
  4145. default:
  4146. break;
  4147. }
  4148. DiskQuotaMsgBox(m_hwndMain,
  4149. iMsg,
  4150. IDS_TITLE_DISK_QUOTA,
  4151. MB_ICONERROR | MB_OK);
  4152. }
  4153. }
  4154. }
  4155. else
  4156. {
  4157. DBGERROR((TEXT("Export: Error 0x%08X returned from QueryGetData."), hResult));
  4158. }
  4159. pIDataObject->Release();
  4160. pIDataObject = NULL;
  4161. }
  4162. else
  4163. {
  4164. DBGERROR((TEXT("Export: Error 0x%08X getting IDataObject."), hResult));
  4165. }
  4166. }
  4167. catch(CAllocException& e)
  4168. {
  4169. if (NULL != pIDataObject)
  4170. pIDataObject->Release();
  4171. throw;
  4172. }
  4173. return SUCCEEDED(hResult);
  4174. }
  4175. ///////////////////////////////////////////////////////////////////////////////
  4176. /* Function: DetailsView::OnCmdViewStatusBar
  4177. Description: Toggles the visibility of the status bar. Invoked when the
  4178. user selects the "Status Bar" menu option.
  4179. Arguments: None.
  4180. Returns: Always returns 0.
  4181. Revision History:
  4182. Date Description Programmer
  4183. -------- --------------------------------------------------- ----------
  4184. 08/20/96 Initial creation. BrianAu
  4185. */
  4186. ///////////////////////////////////////////////////////////////////////////////
  4187. LRESULT
  4188. DetailsView::OnCmdViewStatusBar(
  4189. VOID
  4190. )
  4191. {
  4192. RECT rc;
  4193. m_lvsi.fStatusBar ^= TRUE;
  4194. ShowWindow(m_hwndStatusBar, m_lvsi.fStatusBar ? SW_SHOW : SW_HIDE);
  4195. //
  4196. // Adjust the main window.
  4197. //
  4198. GetWindowRect(m_hwndMain, &rc);
  4199. OnSize(m_hwndMain, WM_SIZE, SIZE_RESTORED, MAKELONG(rc.right-rc.left,rc.bottom-rc.top));
  4200. //
  4201. // Check the menu item to indicate the current status bar state.
  4202. //
  4203. CheckMenuItem(GetMenu(m_hwndMain),
  4204. IDM_VIEW_STATUSBAR,
  4205. MF_BYCOMMAND | (m_lvsi.fStatusBar ? MF_CHECKED : MF_UNCHECKED));
  4206. return 0;
  4207. }
  4208. ///////////////////////////////////////////////////////////////////////////////
  4209. /* Function: DetailsView::OnCmdViewToolBar
  4210. Description: Toggles the visibility of the tool bar. Invoked when the
  4211. user selects the "Tool Bar" menu option.
  4212. Arguments: None.
  4213. Returns: Always returns 0.
  4214. Revision History:
  4215. Date Description Programmer
  4216. -------- --------------------------------------------------- ----------
  4217. 08/20/96 Initial creation. BrianAu
  4218. */
  4219. ///////////////////////////////////////////////////////////////////////////////
  4220. LRESULT
  4221. DetailsView::OnCmdViewToolBar(
  4222. VOID
  4223. )
  4224. {
  4225. RECT rc;
  4226. m_lvsi.fToolBar ^= TRUE;
  4227. ShowWindow(m_hwndToolBar, m_lvsi.fToolBar ? SW_SHOW : SW_HIDE);
  4228. //
  4229. // Adjust the main window.
  4230. //
  4231. GetWindowRect(m_hwndMain, &rc);
  4232. OnSize(m_hwndMain, WM_SIZE, SIZE_RESTORED, MAKELONG(rc.right-rc.left,rc.bottom-rc.top));
  4233. //
  4234. // Check the menu item to indicate the current tool bar state.
  4235. //
  4236. CheckMenuItem(GetMenu(m_hwndMain),
  4237. IDM_VIEW_TOOLBAR,
  4238. MF_BYCOMMAND | (m_lvsi.fToolBar ? MF_CHECKED : MF_UNCHECKED));
  4239. return 0;
  4240. }
  4241. ///////////////////////////////////////////////////////////////////////////////
  4242. /* Function: DetailsView::OnCmdViewShowFolder
  4243. Description: Toggles the visibility of the Domain Name column. Invoked
  4244. when the user selects the "Show Domain" menu option.
  4245. Arguments: None.
  4246. Returns: Always returns 0.
  4247. Revision History:
  4248. Date Description Programmer
  4249. -------- --------------------------------------------------- ----------
  4250. 09/06/96 Initial creation. BrianAu
  4251. */
  4252. ///////////////////////////////////////////////////////////////////////////////
  4253. LRESULT
  4254. DetailsView::OnCmdViewShowFolder(
  4255. VOID
  4256. )
  4257. {
  4258. m_lvsi.fShowFolder ^= TRUE;
  4259. if (m_lvsi.fShowFolder)
  4260. {
  4261. //
  4262. // Insert the folder column and DEACTIVATE listview tooltip.
  4263. // Always add at index 1 then shift it to position 0.
  4264. // User can drag it elsewhere if they like.
  4265. // Because of the listview's icon painting behavior, we only let the
  4266. // "status" column be index 0.
  4267. //
  4268. AddColumn(1, g_rgColumns[DetailsView::idCol_Folder]);
  4269. INT cCols = Header_GetItemCount(m_hwndHeader);
  4270. INT rgColIndicies[DetailsView::idCol_Last];
  4271. INT iTemp = 0;
  4272. DBGASSERT((DetailsView::idCol_Last >= cCols));
  4273. Header_GetOrderArray(m_hwndHeader, cCols, rgColIndicies);
  4274. //
  4275. // Swap the column we just added with column 0.
  4276. //
  4277. iTemp = rgColIndicies[0];
  4278. rgColIndicies[0] = rgColIndicies[1];
  4279. rgColIndicies[1] = iTemp;
  4280. Header_SetOrderArray(m_hwndHeader, cCols, rgColIndicies);
  4281. ActivateListViewToolTip(FALSE);
  4282. }
  4283. else
  4284. {
  4285. //
  4286. // Remove the folder column and ACTIVATE listview tooltip.
  4287. // With the column hidden, users can view a user's folder by hovering
  4288. // over the user's name.
  4289. //
  4290. ActivateListViewToolTip(TRUE);
  4291. RemoveColumn(DetailsView::idCol_Folder);
  4292. }
  4293. //
  4294. // Check/Uncheck the "Show Folder" menu item.
  4295. //
  4296. CheckMenuItem(GetMenu(m_hwndMain),
  4297. IDM_VIEW_SHOWFOLDER,
  4298. MF_BYCOMMAND | (m_lvsi.fShowFolder ? MF_CHECKED : MF_UNCHECKED));
  4299. //
  4300. // If the folder column is hidden, the "Arrange by Folder" menu option
  4301. // is disabled.
  4302. //
  4303. EnableMenuItem_ArrangeByFolder(m_lvsi.fShowFolder);
  4304. //
  4305. // I haven't found a way to do this without unloading and reloading the
  4306. // objects following the new column configuration.
  4307. //
  4308. Refresh();
  4309. return 0;
  4310. }
  4311. ///////////////////////////////////////////////////////////////////////////////
  4312. /* Function: DetailsView::EnableMenuItem_ArrangeByFolder
  4313. Description: Sets the sensitivity of the "by Folder" menu item
  4314. in the "Arrange Items" submenu.
  4315. Arguments:
  4316. bEnable - TRUE = Enable menu item, FALSE = Disable and gray item.
  4317. Returns: Nothing.
  4318. Revision History:
  4319. Date Description Programmer
  4320. -------- --------------------------------------------------- ----------
  4321. 09/08/96 Initial creation. BrianAu
  4322. */
  4323. ///////////////////////////////////////////////////////////////////////////////
  4324. VOID
  4325. DetailsView::EnableMenuItem_ArrangeByFolder(
  4326. BOOL bEnable
  4327. )
  4328. {
  4329. HMENU hMainMenu = GetMenu(m_hwndMain);
  4330. HMENU hViewMenu = GetSubMenu(hMainMenu, iMENUITEM_VIEW);
  4331. HMENU hViewArrangeMenu = GetSubMenu(hViewMenu, iMENUITEM_VIEW_ARRANGE);
  4332. //
  4333. // If these assert, it probably means somebody's changed the
  4334. // menus so that the iMENUITEM_XXXXX constants are no longer correct.
  4335. //
  4336. DBGASSERT((NULL != hViewMenu));
  4337. DBGASSERT((NULL != hViewArrangeMenu));
  4338. EnableMenuItem(hViewArrangeMenu,
  4339. IDM_VIEW_ARRANGE_BYFOLDER,
  4340. MF_BYCOMMAND | (bEnable ? MF_ENABLED : MF_GRAYED));
  4341. }
  4342. ///////////////////////////////////////////////////////////////////////////////
  4343. /* Function: DetailsView::EnableMenuItem_Undo
  4344. Description: Sets the sensitivity of the "Undo" menu item
  4345. in the "Edit" submenu.
  4346. Arguments:
  4347. bEnable - TRUE = Enable menu item, FALSE = Disable and gray item.
  4348. Returns: Nothing.
  4349. Revision History:
  4350. Date Description Programmer
  4351. -------- --------------------------------------------------- ----------
  4352. 10/08/96 Initial creation. BrianAu
  4353. 10/22/96 Replaced Assert() with nested if's. BrianAu
  4354. Tester hit assert via WM_COMMAND when closing
  4355. details view.
  4356. */
  4357. ///////////////////////////////////////////////////////////////////////////////
  4358. VOID
  4359. DetailsView::EnableMenuItem_Undo(
  4360. BOOL bEnable
  4361. )
  4362. {
  4363. HMENU hMainMenu = GetMenu(m_hwndMain);
  4364. if (NULL != hMainMenu)
  4365. {
  4366. HMENU hEditMenu = GetSubMenu(hMainMenu, iMENUITEM_EDIT);
  4367. if (NULL != hEditMenu)
  4368. {
  4369. EnableMenuItem(hEditMenu,
  4370. IDM_EDIT_UNDO,
  4371. MF_BYCOMMAND | (bEnable ? MF_ENABLED : MF_GRAYED));
  4372. SendMessage(m_hwndToolBar, TB_ENABLEBUTTON, IDM_EDIT_UNDO, MAKELONG(bEnable, 0));
  4373. }
  4374. }
  4375. }
  4376. ///////////////////////////////////////////////////////////////////////////////
  4377. /* Function: DetailsView::ShowItemCountInStatusBar
  4378. Description: Displays the current count of selected items in the status bar.
  4379. This is what is displayed in the status bar when a menu item is not
  4380. currently selected.
  4381. Arguments: None.
  4382. Returns: Always returns 0.
  4383. Revision History:
  4384. Date Description Programmer
  4385. -------- --------------------------------------------------- ----------
  4386. 08/20/96 Initial creation. BrianAu
  4387. 12/16/96 Appended "incorrect data" warning to status bar BrianAu
  4388. text when quotas are disabled on system.
  4389. */
  4390. ///////////////////////////////////////////////////////////////////////////////
  4391. LRESULT
  4392. DetailsView::ShowItemCountInStatusBar(
  4393. VOID
  4394. )
  4395. {
  4396. DWORD cTotalItems = m_UserList.Count();
  4397. DWORD cSelectedItems = ListView_GetSelectedCount(m_hwndListView);
  4398. CString strText(g_hInstDll, IDS_STATUSBAR_ITEMCOUNT, cTotalItems, cSelectedItems);
  4399. SendMessage(m_hwndStatusBar, SB_SETTEXT, 0, (LPARAM)((LPCTSTR)strText));
  4400. return 0;
  4401. }
  4402. ///////////////////////////////////////////////////////////////////////////////
  4403. /* Function: DetailsView::ShowMenuTextInStatusBar
  4404. Description: Displays the description of the currently-selected menu
  4405. item in the status bar.
  4406. Arguments: None.
  4407. Returns: Always returns 0.
  4408. Revision History:
  4409. Date Description Programmer
  4410. -------- --------------------------------------------------- ----------
  4411. 08/20/96 Initial creation. BrianAu
  4412. */
  4413. ///////////////////////////////////////////////////////////////////////////////
  4414. LRESULT
  4415. DetailsView::ShowMenuTextInStatusBar(
  4416. DWORD idMenuOption
  4417. )
  4418. {
  4419. TCHAR szText[MAX_PATH];
  4420. LoadString(g_hInstDll, idMenuOption, szText, ARRAYSIZE(szText));
  4421. SendMessage(m_hwndStatusBar, SB_SETTEXT, 0, (LPARAM)szText);
  4422. return 0;
  4423. }
  4424. ///////////////////////////////////////////////////////////////////////////////
  4425. /* Function: DetailsView::Refresh
  4426. Description: Refreshes the view by re-loading the objects.
  4427. Arguments: bInvalidateCache - true == invalidate all entries in the
  4428. SID-name cache. Default is false.
  4429. Returns: Always returns 0.
  4430. Revision History:
  4431. Date Description Programmer
  4432. -------- --------------------------------------------------- ----------
  4433. 08/20/96 Initial creation. BrianAu
  4434. 02/21/97 Ownerdata listview. BrianAu
  4435. 10/10/98 Added bInvalidateCache argument. BrianAu
  4436. */
  4437. ///////////////////////////////////////////////////////////////////////////////
  4438. LRESULT
  4439. DetailsView::Refresh(
  4440. bool bInvalidateCache // optional. default is false.
  4441. )
  4442. {
  4443. CAutoWaitCursor waitcursor;
  4444. if (bInvalidateCache)
  4445. m_pQuotaControl->InvalidateSidNameCache();
  4446. InvalidateRect(m_hwndListView, NULL, TRUE);
  4447. CAutoSetRedraw autoredraw(m_hwndListView, false);
  4448. ReleaseObjects();
  4449. autoredraw.Set(true);
  4450. LoadObjects();
  4451. ListView_SetItemCountEx(m_hwndListView,
  4452. m_UserList.Count(),
  4453. LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL);
  4454. SortObjects(m_iLastColSorted, m_fSortDirection);
  4455. ShowItemCountInStatusBar();
  4456. FocusOnSomething();
  4457. return 0;
  4458. }
  4459. ///////////////////////////////////////////////////////////////////////////////
  4460. /* Function: DetailsView::FocusOnSomething
  4461. Description: Ensures that one or more listview items have the focus
  4462. highlighting.
  4463. Arguments: None.
  4464. Returns: Nothing.
  4465. Revision History:
  4466. Date Description Programmer
  4467. -------- --------------------------------------------------- ----------
  4468. 09/09/96 Initial creation. BrianAu
  4469. */
  4470. ///////////////////////////////////////////////////////////////////////////////
  4471. VOID
  4472. DetailsView::FocusOnSomething(
  4473. VOID
  4474. )
  4475. {
  4476. INT iFocus;
  4477. iFocus = ListView_GetNextItem(m_hwndListView, -1, LVNI_FOCUSED);
  4478. if (-1 == iFocus)
  4479. iFocus = 0;
  4480. ListView_SetItemState(m_hwndListView, iFocus, LVIS_FOCUSED | LVIS_SELECTED,
  4481. LVIS_FOCUSED | LVIS_SELECTED)
  4482. }
  4483. ///////////////////////////////////////////////////////////////////////////////
  4484. /* Function: DetailsView::CalcPctQuotaUsed
  4485. Description: Calculates the percent quota used for a given user. The
  4486. value returned is an integer.
  4487. Arguments:
  4488. pUser - Address of IDiskQuotaUser interface for user object.
  4489. pdwPct - Address of DWORD to receive the percentage value.
  4490. If the method returns div-by-zero, this value is set to ~0.
  4491. This lets a caller sort erroneous values from valid values.
  4492. The (~0 - 1) return value is used so that NOLIMIT users
  4493. are grouped separate from 0 limit users when sorted on % used.
  4494. Both are using 0% of their quota but it looks better if
  4495. they are each grouped separately.
  4496. Returns:
  4497. NO_ERROR - Success.
  4498. STATUS_INTEGER_DIVIDE_BY_ZERO - The user's quota limit was 0.
  4499. Revision History:
  4500. Date Description Programmer
  4501. -------- --------------------------------------------------- ----------
  4502. 08/20/96 Initial creation. BrianAu
  4503. */
  4504. ///////////////////////////////////////////////////////////////////////////////
  4505. HRESULT
  4506. DetailsView::CalcPctQuotaUsed(
  4507. PDISKQUOTA_USER pUser,
  4508. LPDWORD pdwPct
  4509. )
  4510. {
  4511. LONGLONG llUsed;
  4512. LONGLONG llLimit;
  4513. HRESULT hResult = E_FAIL;
  4514. DBGASSERT((NULL != pUser));
  4515. DBGASSERT((NULL != pdwPct));
  4516. pUser->GetQuotaUsed(&llUsed);
  4517. pUser->GetQuotaLimit(&llLimit);
  4518. if (NOLIMIT == llLimit)
  4519. {
  4520. *pdwPct = (DWORD)~0 - 1; // No quota limit for user.
  4521. }
  4522. else if (0 < llLimit)
  4523. {
  4524. *pdwPct = (INT)((llUsed * 100) / llLimit);
  4525. hResult = NO_ERROR;
  4526. }
  4527. else
  4528. {
  4529. //
  4530. // Limit is 0. Would produce div-by-zero.
  4531. //
  4532. *pdwPct = (DWORD)~0;
  4533. }
  4534. return hResult;
  4535. }
  4536. ///////////////////////////////////////////////////////////////////////////////
  4537. /* Function: DetailsView::AddUser
  4538. Description: Adds a user object to the listview. Note that this is used
  4539. for adding a single user object such as in an "add user" operation.
  4540. The method LoadObjects is used to load the whole listview. It's
  4541. more efficient than calling this for each user.
  4542. Arguments:
  4543. pUser - Address of user's IDiskQuotaUser interface.
  4544. Returns:
  4545. TRUE - User was added.
  4546. FALSE - User wasn't added.
  4547. Revision History:
  4548. Date Description Programmer
  4549. -------- --------------------------------------------------- ----------
  4550. 09/30/96 Initial creation. BrianAu
  4551. */
  4552. ///////////////////////////////////////////////////////////////////////////////
  4553. BOOL
  4554. DetailsView::AddUser(
  4555. PDISKQUOTA_USER pUser
  4556. )
  4557. {
  4558. BOOL bResult = FALSE;
  4559. LV_ITEM item;
  4560. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
  4561. item.state = 0;
  4562. item.stateMask = 0;
  4563. item.iSubItem = 0;
  4564. item.pszText = LPSTR_TEXTCALLBACK;
  4565. item.iImage = I_IMAGECALLBACK;
  4566. item.iItem = 0;
  4567. m_UserList.Insert((LPVOID)pUser);
  4568. if (-1 != ListView_InsertItem(m_hwndListView, &item))
  4569. {
  4570. bResult = TRUE;
  4571. }
  4572. return bResult;
  4573. }
  4574. ///////////////////////////////////////////////////////////////////////////////
  4575. /* Function: DetailsView::FindUserByName
  4576. Description: Locate a specified user in the listview by account name.
  4577. Name comparison is case-insensitive.
  4578. Arguments:
  4579. pszUserName - Account name for user.
  4580. ppIUser [optional] - Address of IDiskQuotaUser pointer variable to
  4581. receive the address of the user object.
  4582. Returns:
  4583. -1 = account name not found.
  4584. Otherwise, returns index of item in listview.
  4585. Revision History:
  4586. Date Description Programmer
  4587. -------- --------------------------------------------------- ----------
  4588. 05/23/97 Initial creation. BrianAu
  4589. */
  4590. ///////////////////////////////////////////////////////////////////////////////
  4591. INT
  4592. DetailsView::FindUserByName(
  4593. LPCTSTR pszLogonName,
  4594. PDISKQUOTA_USER *ppIUser // [optional]
  4595. )
  4596. {
  4597. INT iItem = -1;
  4598. m_UserList.Lock();
  4599. try
  4600. {
  4601. INT cUsers = m_UserList.Count();
  4602. PDISKQUOTA_USER pUser = NULL;
  4603. //
  4604. // Find the user that matches pszUserName.
  4605. //
  4606. for (INT i = 0; i < cUsers && -1 == iItem; i++)
  4607. {
  4608. if (m_UserList.Retrieve((LPVOID *)&pUser, i))
  4609. {
  4610. //
  4611. // Get name from listview item.
  4612. //
  4613. if (NULL != pUser)
  4614. {
  4615. TCHAR szLogonName[MAX_USERNAME];
  4616. pUser->GetName(NULL, 0,
  4617. szLogonName, ARRAYSIZE(szLogonName),
  4618. NULL, 0);
  4619. if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
  4620. NORM_IGNORECASE,
  4621. szLogonName, -1,
  4622. pszLogonName, -1))
  4623. {
  4624. iItem = i;
  4625. if (NULL != ppIUser)
  4626. *ppIUser = pUser;
  4627. }
  4628. }
  4629. }
  4630. }
  4631. }
  4632. catch(CAllocException& e)
  4633. {
  4634. m_UserList.ReleaseLock();
  4635. throw;
  4636. }
  4637. m_UserList.ReleaseLock();
  4638. return iItem;
  4639. }
  4640. ///////////////////////////////////////////////////////////////////////////////
  4641. /* Function: DetailsView::FindUserBySid
  4642. Description: Locate a specified user in the listview by SID.
  4643. Arguments:
  4644. pSid - Address of buffer containing key SID.
  4645. ppIUser [optional] - Address of IDiskQuotaUser pointer variable to
  4646. receive the address of the user object.
  4647. Returns:
  4648. -1 = Record not found.
  4649. Otherwise, returns index of item in listview.
  4650. Revision History:
  4651. Date Description Programmer
  4652. -------- --------------------------------------------------- ----------
  4653. 05/23/97 Initial creation. BrianAu
  4654. */
  4655. ///////////////////////////////////////////////////////////////////////////////
  4656. INT
  4657. DetailsView::FindUserBySid(
  4658. LPBYTE pbSid,
  4659. PDISKQUOTA_USER *ppIUser // [optional]
  4660. )
  4661. {
  4662. INT iItem = -1;
  4663. m_UserList.Lock();
  4664. try
  4665. {
  4666. INT cUsers = m_UserList.Count();
  4667. PDISKQUOTA_USER pUser = NULL;
  4668. //
  4669. // Find the user that matches pszUserName.
  4670. //
  4671. for (INT i = 0; i < cUsers && -1 == iItem; i++)
  4672. {
  4673. if (m_UserList.Retrieve((LPVOID *)&pUser, i))
  4674. {
  4675. //
  4676. // Get SID from listview item.
  4677. //
  4678. if (NULL != pUser)
  4679. {
  4680. BYTE Sid[MAX_SID_LEN];
  4681. pUser->GetSid((LPBYTE)&Sid, ARRAYSIZE(Sid));
  4682. if (EqualSid((LPBYTE)&Sid, pbSid))
  4683. {
  4684. iItem = i;
  4685. if (NULL != ppIUser)
  4686. *ppIUser = pUser;
  4687. }
  4688. }
  4689. }
  4690. }
  4691. }
  4692. catch(CAllocException& e)
  4693. {
  4694. m_UserList.ReleaseLock();
  4695. throw;
  4696. }
  4697. m_UserList.ReleaseLock();
  4698. return iItem;
  4699. }
  4700. //
  4701. // Locate a user in the listview based on it's object pointer.
  4702. //
  4703. INT
  4704. DetailsView::FindUserByObjPtr(
  4705. PDISKQUOTA_USER pUserKey
  4706. )
  4707. {
  4708. INT iItem = -1;
  4709. m_UserList.Lock();
  4710. try
  4711. {
  4712. INT cUsers = m_UserList.Count();
  4713. PDISKQUOTA_USER pUser = NULL;
  4714. //
  4715. // Find the user that matches pszUserName.
  4716. //
  4717. for (INT i = 0; i < cUsers && -1 == iItem; i++)
  4718. {
  4719. if (m_UserList.Retrieve((LPVOID *)&pUser, i))
  4720. {
  4721. if (NULL != pUser && pUserKey == pUser)
  4722. {
  4723. iItem = i;
  4724. }
  4725. }
  4726. }
  4727. }
  4728. catch(CAllocException& e)
  4729. {
  4730. m_UserList.ReleaseLock();
  4731. throw;
  4732. }
  4733. m_UserList.ReleaseLock();
  4734. return iItem;
  4735. }
  4736. ///////////////////////////////////////////////////////////////////////////////
  4737. /* Function: DetailsView::GotoUserName
  4738. Description: Locate a specified user in the listview. If found, highlight
  4739. the row. The search is case-insensitive. This function was originally
  4740. designed to work with the "Find User" feature so that when a record
  4741. is located, it is made visible in the view and highlighted.
  4742. Arguments:
  4743. pszUserName - Account name for user.
  4744. Returns:
  4745. TRUE = Record found.
  4746. FALSE = Not found.
  4747. Revision History:
  4748. Date Description Programmer
  4749. -------- --------------------------------------------------- ----------
  4750. 05/20/97 Initial creation. BrianAu
  4751. */
  4752. ///////////////////////////////////////////////////////////////////////////////
  4753. BOOL
  4754. DetailsView::GotoUserName(
  4755. LPCTSTR pszUserName
  4756. )
  4757. {
  4758. INT iUser = FindUserByName(pszUserName);
  4759. if (-1 != iUser)
  4760. {
  4761. //
  4762. // Found a match (case-insensitive).
  4763. //
  4764. // Select the item specified by the user.
  4765. // Note that we leave any selected items selected.
  4766. // Users may use the find feature to select a set of
  4767. // non-contiguous quota records in the listview.
  4768. //
  4769. ListView_EnsureVisible(m_hwndListView, iUser, FALSE);
  4770. ListView_SetItemState(m_hwndListView, iUser, LVIS_FOCUSED | LVIS_SELECTED,
  4771. LVIS_FOCUSED | LVIS_SELECTED);
  4772. }
  4773. return (-1 != iUser);
  4774. }
  4775. ///////////////////////////////////////////////////////////////////////////////
  4776. /* Function: DetailsView::GetConnectionPoint
  4777. Description: Retrieves the IDiskQuotaEvents connection point from
  4778. the quota control object. This is the connection point through which
  4779. the asynchronous user name change events are delivered as names
  4780. are resolved by the network DC.
  4781. Arguments: None.
  4782. Returns:
  4783. Revision History:
  4784. Date Description Programmer
  4785. -------- --------------------------------------------------- ----------
  4786. 08/20/96 Initial creation. BrianAu
  4787. */
  4788. ///////////////////////////////////////////////////////////////////////////////
  4789. IConnectionPoint *
  4790. DetailsView::GetConnectionPoint(
  4791. VOID
  4792. )
  4793. {
  4794. HRESULT hResult = NO_ERROR;
  4795. IConnectionPoint *pCP = NULL;
  4796. if (NULL != m_pQuotaControl)
  4797. {
  4798. IConnectionPointContainer *pCPC = NULL;
  4799. hResult = m_pQuotaControl->QueryInterface(IID_IConnectionPointContainer,
  4800. (LPVOID *)&pCPC);
  4801. if (SUCCEEDED(hResult))
  4802. {
  4803. hResult = pCPC->FindConnectionPoint(IID_IDiskQuotaEvents, &pCP);
  4804. pCPC->Release();
  4805. if (FAILED(hResult))
  4806. pCP = NULL;
  4807. }
  4808. }
  4809. return pCP;
  4810. }
  4811. ///////////////////////////////////////////////////////////////////////////////
  4812. /* Function: DetailsView::ConnectEventSink
  4813. Description: Connects the event sink (DetailsView) from the quota
  4814. controller's IDiskQuotaEvents connection point object.
  4815. Arguments: None.
  4816. Returns:
  4817. NO_ERROR - Success.
  4818. E_FAIL - Failed.
  4819. Revision History:
  4820. Date Description Programmer
  4821. -------- --------------------------------------------------- ----------
  4822. 08/20/96 Initial creation. BrianAu
  4823. */
  4824. ///////////////////////////////////////////////////////////////////////////////
  4825. HRESULT
  4826. DetailsView::ConnectEventSink(
  4827. VOID
  4828. )
  4829. {
  4830. HRESULT hResult = NO_ERROR;
  4831. IConnectionPoint *pConnPt = GetConnectionPoint();
  4832. if (NULL != pConnPt)
  4833. {
  4834. hResult = pConnPt->Advise((LPUNKNOWN)static_cast<IDataObject *>(this), &m_dwEventCookie);
  4835. pConnPt->Release();
  4836. DBGPRINT((DM_VIEW, DL_MID, TEXT("LISTVIEW - Connected event sink. Cookie = %d"), m_dwEventCookie));
  4837. }
  4838. else
  4839. hResult = E_FAIL;
  4840. return hResult;
  4841. }
  4842. ///////////////////////////////////////////////////////////////////////////////
  4843. /* Function: DetailsView::DisconnectEventSink
  4844. Description: Disconnects the event sink (DetailsView) from the quota
  4845. controller's IDiskQuotaEvents connection point object.
  4846. Arguments: None.
  4847. Returns:
  4848. NO_ERROR - Success.
  4849. E_FAIL - Failed.
  4850. Revision History:
  4851. Date Description Programmer
  4852. -------- --------------------------------------------------- ----------
  4853. 08/20/96 Initial creation. BrianAu
  4854. */
  4855. ///////////////////////////////////////////////////////////////////////////////
  4856. HRESULT
  4857. DetailsView::DisconnectEventSink(
  4858. VOID
  4859. )
  4860. {
  4861. HRESULT hResult = NO_ERROR;
  4862. DBGPRINT((DM_VIEW, DL_MID, TEXT("LISTVIEW - Disconnecting event sink. Cookie = %d"), m_dwEventCookie));
  4863. if (0 != m_dwEventCookie)
  4864. {
  4865. IConnectionPoint *pConnPt = GetConnectionPoint();
  4866. if (NULL != pConnPt)
  4867. {
  4868. hResult = pConnPt->Unadvise(m_dwEventCookie);
  4869. if (SUCCEEDED(hResult))
  4870. {
  4871. m_dwEventCookie = 0;
  4872. }
  4873. pConnPt->Release();
  4874. }
  4875. else
  4876. hResult = E_FAIL;
  4877. }
  4878. return hResult;
  4879. }
  4880. ///////////////////////////////////////////////////////////////////////////////
  4881. /* Function: DetailsView::InitLVStateInfo
  4882. Description: Initializes an LV_STATE_INFO structure to default values.
  4883. NOTE: This method is declared static so that it can be called
  4884. without a DetailsView object (not needed).
  4885. If you want to change any listview state defaults, this is the
  4886. place to do it.
  4887. Arguments:
  4888. plvsi - Address of an LV_STATE_INFO structure to be initialized.
  4889. Returns: Nothing.
  4890. Revision History:
  4891. Date Description Programmer
  4892. -------- --------------------------------------------------- ----------
  4893. 10/08/96 Initial creation. BrianAu
  4894. */
  4895. ///////////////////////////////////////////////////////////////////////////////
  4896. VOID
  4897. DetailsView::InitLVStateInfo(
  4898. PLV_STATE_INFO plvsi
  4899. )
  4900. {
  4901. ZeroMemory(plvsi, sizeof(*plvsi));
  4902. plvsi->cb = sizeof(*plvsi);
  4903. plvsi->wVersion = wLV_STATE_INFO_VERSION;
  4904. plvsi->fToolBar = 1; // Default to toolbar visible.
  4905. plvsi->fStatusBar = 1; // Default to statusbar visible.
  4906. plvsi->iLastColSorted = 0; // Default to sort first col.
  4907. plvsi->fSortDirection = 1; // Default to ascending sort.
  4908. for (UINT i = 0; i < DetailsView::idCol_Last; i++)
  4909. plvsi->rgColIndices[i] = i;
  4910. }
  4911. ///////////////////////////////////////////////////////////////////////////////
  4912. /* Function: DetailsView::IsValidLVStateInfo
  4913. Description: Validates the contents of a LV_STATE_INFO structure.
  4914. NOTE: This method is declared static so that it can be called
  4915. without a DetailsView object (not needed).
  4916. Arguments:
  4917. plvsi - Address of an LV_STATE_INFO structure to be validated.
  4918. Returns: Nothing.
  4919. Revision History:
  4920. Date Description Programmer
  4921. -------- --------------------------------------------------- ----------
  4922. 12/10/96 Initial creation. BrianAu
  4923. */
  4924. ///////////////////////////////////////////////////////////////////////////////
  4925. BOOL
  4926. DetailsView::IsValidLVStateInfo(
  4927. PLV_STATE_INFO plvsi
  4928. )
  4929. {
  4930. BOOL bResult = FALSE;
  4931. INT i = 0;
  4932. //
  4933. // Validate structure size member.
  4934. //
  4935. if (plvsi->cb != sizeof(LV_STATE_INFO))
  4936. goto info_invalid;
  4937. //
  4938. // Validate version.
  4939. //
  4940. if (wLV_STATE_INFO_VERSION != plvsi->wVersion)
  4941. goto info_invalid;
  4942. //
  4943. // Validate iLastSorted member.
  4944. //
  4945. if (!(plvsi->iLastColSorted >= 0 && plvsi->iLastColSorted < DetailsView::idCol_Last))
  4946. goto info_invalid;
  4947. //
  4948. // Validate each of the column index members. Used for ordering columns.
  4949. //
  4950. for (i = 0; i < DetailsView::idCol_Last; i++)
  4951. {
  4952. if (!(plvsi->rgColIndices[i] >= 0 && plvsi->rgColIndices[i] < DetailsView::idCol_Last))
  4953. goto info_invalid;
  4954. }
  4955. bResult = TRUE;
  4956. info_invalid:
  4957. return bResult;
  4958. }
  4959. ///////////////////////////////////////////////////////////////////////////////
  4960. /* Function: DetailsView::GetColumnIds
  4961. Description: Retrieves a list of IDs for the visible columns in the list.
  4962. A client can use this list to request report items from the
  4963. GetReportXXXXX methods below.
  4964. Arguments:
  4965. prgColIds - Pointer to an array of INTs to receive the column IDs.
  4966. cColIds - Size of the destination array.
  4967. Returns: Number of IDs written to destination array.
  4968. Revision History:
  4969. Date Description Programmer
  4970. -------- --------------------------------------------------- ----------
  4971. 10/08/96 Initial creation. BrianAu
  4972. */
  4973. ///////////////////////////////////////////////////////////////////////////////
  4974. UINT
  4975. DetailsView::GetColumnIds(
  4976. INT *prgColIds,
  4977. INT cColIds
  4978. )
  4979. {
  4980. INT cHdrCols = Header_GetItemCount(m_hwndHeader);
  4981. INT rgiSubItem[DetailsView::idCol_Last];
  4982. INT i = 0;
  4983. if (Header_GetOrderArray(m_hwndHeader, cHdrCols, rgiSubItem))
  4984. {
  4985. for (i = 0; i < cHdrCols && i < cColIds; i++)
  4986. {
  4987. *(prgColIds + i) = m_ColMap.SubItemToId(rgiSubItem[i]);
  4988. }
  4989. }
  4990. return i;
  4991. }
  4992. ///////////////////////////////////////////////////////////////////////////////
  4993. /* Function: DetailsView::GetNextSelectedItemIndex
  4994. Description: Retrieves the index of a selected item. The search starts
  4995. with the index supplied in the iRow argument. Therefore, the following
  4996. loop will find all selected items:
  4997. INT iItem = -1;
  4998. while(1)
  4999. {
  5000. iItem = GetNextSelectedItemIndex(iItem);
  5001. if (-1 == iItem)
  5002. break;
  5003. //
  5004. // Do something with item.
  5005. //
  5006. }
  5007. Arguments:
  5008. iRow - Row where to start search. The row itself is exluded from
  5009. the search. -1 starts search from the head of the listview.
  5010. Returns:
  5011. 0-based index of next selected item if found.
  5012. -1 if no more selected items.
  5013. Revision History:
  5014. Date Description Programmer
  5015. -------- --------------------------------------------------- ----------
  5016. 05/28/97 Initial creation. BrianAu
  5017. */
  5018. ///////////////////////////////////////////////////////////////////////////////
  5019. INT
  5020. DetailsView::GetNextSelectedItemIndex(INT iRow)
  5021. {
  5022. return ListView_GetNextItem(m_hwndListView, iRow, LVNI_ALL | LVNI_SELECTED);
  5023. }
  5024. ///////////////////////////////////////////////////////////////////////////////
  5025. /* Function: DetailsView::GetReportItem
  5026. Description: Retrieve a data item for a drag-drop/clipboard report.
  5027. This method is patterned after the GetDispInfo_XXX methods but is
  5028. taylored to placing data on a Stream object.
  5029. Arguments:
  5030. iRow - Row where to begin search for next selected item in listview.
  5031. For the first call, specify -1 to begin the search with the
  5032. first item. Subsequent calls should specify the value returned
  5033. from the previous call to GetReportItem.
  5034. iColId - Item's column ID (idCol_Folder, idCol_Name etc).
  5035. pItem - Address of an LV_REPORT_ITEM structure. This structure
  5036. is used to return the data to the caller and also to specify the
  5037. desired format for numeric values. Some report formats want
  5038. all data in text format (i.e. CF_TEXT) while other binary formats
  5039. want numeric data in numeric format (i.e. XlTable).
  5040. Returns: TRUE = Retrieved row/col data.
  5041. FALSE = Invalid row or column index.
  5042. Revision History:
  5043. Date Description Programmer
  5044. -------- --------------------------------------------------- ----------
  5045. 10/08/96 Initial creation. BrianAu
  5046. */
  5047. ///////////////////////////////////////////////////////////////////////////////
  5048. BOOL
  5049. DetailsView::GetReportItem(
  5050. UINT iRow,
  5051. UINT iColId,
  5052. PLV_REPORT_ITEM pItem
  5053. )
  5054. {
  5055. DBGASSERT((NULL != pItem));
  5056. BOOL bResult = FALSE;
  5057. PDISKQUOTA_USER pUser = NULL;
  5058. if (m_UserList.Retrieve((LPVOID *)&pUser, iRow))
  5059. {
  5060. LONGLONG llValue;
  5061. DBGASSERT((NULL != pUser));
  5062. bResult = TRUE;
  5063. switch(iColId)
  5064. {
  5065. case DetailsView::idCol_Folder:
  5066. {
  5067. DWORD dwAccountStatus = 0;
  5068. pUser->GetAccountStatus(&dwAccountStatus);
  5069. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
  5070. {
  5071. pUser->GetName(pItem->pszText, pItem->cchMaxText,
  5072. NULL, 0,
  5073. NULL, 0);
  5074. }
  5075. else
  5076. {
  5077. //
  5078. // Folder name not resolved. User name column will
  5079. // contain status text.
  5080. //
  5081. lstrcpyn(pItem->pszText, TEXT(""), pItem->cchMaxText);
  5082. }
  5083. pItem->fType = LVRI_TEXT;
  5084. break;
  5085. }
  5086. case DetailsView::idCol_Name:
  5087. {
  5088. DWORD dwAccountStatus = 0;
  5089. CString strNameText;
  5090. pUser->GetAccountStatus(&dwAccountStatus);
  5091. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
  5092. {
  5093. //
  5094. // User's name has been resolved.
  5095. //
  5096. pItem->fType = LVRI_TEXT;
  5097. pUser->GetName(NULL, 0,
  5098. NULL, 0,
  5099. strNameText.GetBuffer(MAX_USERNAME), MAX_USERNAME);
  5100. strNameText.ReleaseBuffer();
  5101. }
  5102. else
  5103. {
  5104. //
  5105. // User's name not resolved. Use a status message.
  5106. //
  5107. switch(dwAccountStatus)
  5108. {
  5109. case DISKQUOTA_USER_ACCOUNT_UNRESOLVED:
  5110. strNameText = m_strAccountUnresolved;
  5111. break;
  5112. case DISKQUOTA_USER_ACCOUNT_UNKNOWN:
  5113. strNameText = m_strAccountUnknown;
  5114. break;
  5115. case DISKQUOTA_USER_ACCOUNT_INVALID:
  5116. strNameText = m_strAccountInvalid;
  5117. break;
  5118. case DISKQUOTA_USER_ACCOUNT_DELETED:
  5119. strNameText = m_strAccountDeleted;
  5120. break;
  5121. case DISKQUOTA_USER_ACCOUNT_UNAVAILABLE:
  5122. strNameText = m_strAccountUnavailable;
  5123. break;
  5124. }
  5125. }
  5126. lstrcpyn(pItem->pszText, strNameText, pItem->cchMaxText);
  5127. break;
  5128. }
  5129. case DetailsView::idCol_LogonName:
  5130. {
  5131. DBGASSERT((NULL != pUser));
  5132. DWORD dwAccountStatus = 0;
  5133. CString strNameText;
  5134. pUser->GetAccountStatus(&dwAccountStatus);
  5135. if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
  5136. {
  5137. //
  5138. // User's name has been resolved.
  5139. //
  5140. pItem->fType = LVRI_TEXT;
  5141. pUser->GetName(NULL, 0,
  5142. strNameText.GetBuffer(MAX_USERNAME), MAX_USERNAME,
  5143. NULL, 0);
  5144. strNameText.ReleaseBuffer();
  5145. }
  5146. lstrcpyn(pItem->pszText, strNameText, pItem->cchMaxText);
  5147. break;
  5148. }
  5149. case DetailsView::idCol_Status:
  5150. //
  5151. // Return a text string to represent the icon shown
  5152. // in the "Status" column.
  5153. //
  5154. DBGASSERT((NULL != pUser));
  5155. switch(GetUserQuotaState(pUser))
  5156. {
  5157. case iUSERSTATE_OK:
  5158. lstrcpyn(pItem->pszText, m_strStatusOK, pItem->cchMaxText);
  5159. break;
  5160. case iUSERSTATE_WARNING:
  5161. lstrcpyn(pItem->pszText, m_strStatusWarning, pItem->cchMaxText);
  5162. break;
  5163. default:
  5164. DBGASSERT((0));
  5165. //
  5166. // Fall through.
  5167. //
  5168. case iUSERSTATE_OVERLIMIT:
  5169. lstrcpyn(pItem->pszText, m_strStatusOverlimit, pItem->cchMaxText);
  5170. break;
  5171. }
  5172. pItem->fType = LVRI_TEXT;
  5173. break;
  5174. //
  5175. // For the following numeric columns, first get the data then
  5176. // jump to fmt_byte_count to format it as requested. Note that
  5177. // all numeric values are expressed in megabytes. This is so they
  5178. // all have the same units to help with ordering in a spreadsheet.
  5179. // Otherwise, sorting would not be possible. This is also why we
  5180. // include the "(MB)" in the report column titles.
  5181. //
  5182. case DetailsView::idCol_AmtUsed:
  5183. pUser->GetQuotaUsed(&llValue);
  5184. goto fmt_byte_count;
  5185. case DetailsView::idCol_Threshold:
  5186. pUser->GetQuotaThreshold(&llValue);
  5187. goto fmt_byte_count;
  5188. case DetailsView::idCol_Limit:
  5189. pUser->GetQuotaLimit(&llValue);
  5190. fmt_byte_count:
  5191. //
  5192. // Format the byte count for the requested data type (text vs. numeric).
  5193. //
  5194. switch(pItem->fType)
  5195. {
  5196. case LVRI_NUMBER:
  5197. pItem->fType = LVRI_REAL;
  5198. if (NOLIMIT == llValue)
  5199. pItem->dblValue = -1.0; // Indicates to caller "No Limit".
  5200. else
  5201. pItem->dblValue = XBytes::ConvertFromBytes(llValue, XBytes::e_Mega);
  5202. break;
  5203. case LVRI_TEXT:
  5204. if (NOLIMIT == llValue)
  5205. lstrcpyn(pItem->pszText, m_strNoLimit, pItem->cchMaxText);
  5206. else
  5207. XBytes::FormatByteCountForDisplay(llValue,
  5208. pItem->pszText,
  5209. pItem->cchMaxText,
  5210. XBytes::e_Mega);
  5211. //
  5212. // Fall through.
  5213. //
  5214. default:
  5215. break;
  5216. }
  5217. break;
  5218. case DetailsView::idCol_PctUsed:
  5219. {
  5220. HRESULT hResult = CalcPctQuotaUsed(pUser, &pItem->dwValue);
  5221. //
  5222. // Format the percent value for the requested data type (text vs. numeric).
  5223. // If a percentage can't be calculated (0 denominator), return -2 as an
  5224. // INT value or "N/A" as a text value.
  5225. //
  5226. switch(pItem->fType)
  5227. {
  5228. case LVRI_NUMBER:
  5229. pItem->fType = LVRI_INT;
  5230. if (FAILED(hResult))
  5231. pItem->dwValue = (DWORD)-2; // Indicates to caller "N/A".
  5232. break;
  5233. case LVRI_TEXT:
  5234. if (FAILED(hResult))
  5235. lstrcpyn(pItem->pszText, m_strNotApplicable, pItem->cchMaxText);
  5236. else
  5237. wsprintf(pItem->pszText, TEXT("%d"), pItem->dwValue);
  5238. default:
  5239. break;
  5240. }
  5241. break;
  5242. }
  5243. default:
  5244. bResult = FALSE;
  5245. break;
  5246. }
  5247. }
  5248. return bResult;
  5249. }
  5250. ///////////////////////////////////////////////////////////////////////////////
  5251. /* Function: DetailsView::GetReportTitle
  5252. Description: Retrieves a title for a report. Uses the listview window
  5253. title.
  5254. Arguments:
  5255. pszDest - Address of destination character buffer.
  5256. cchDest - Size of destination buffer in characters.
  5257. Returns: Nothing.
  5258. Revision History:
  5259. Date Description Programmer
  5260. -------- --------------------------------------------------- ----------
  5261. 10/08/96 Initial creation. BrianAu
  5262. */
  5263. ///////////////////////////////////////////////////////////////////////////////
  5264. VOID
  5265. DetailsView::GetReportTitle(
  5266. LPTSTR pszDest,
  5267. UINT cchDest
  5268. )
  5269. {
  5270. //
  5271. // This is simple. Just use the details view title.
  5272. // FEATURE: Could be enhanced to include the date/time but that will
  5273. // require localization considerations.
  5274. //
  5275. GetWindowText(m_hwndMain, pszDest, cchDest);
  5276. }
  5277. ///////////////////////////////////////////////////////////////////////////////
  5278. /* Function: DetailsView::GetReportColHeader
  5279. Description: Retrieves a title for a report column. Note that the titles
  5280. may differ from those used in the listview. Specifically for the
  5281. numeric columns. In the listview, numeric column entries include
  5282. units (bytes, KB, MB etc.). In a report, these numeric values are
  5283. all expressed in MB. Therefore, the units must be included in the
  5284. title string.
  5285. Arguments:
  5286. iColId - ID of column requested (idCol_Folder, idCol_Name etc.)
  5287. pszDest - Address of destination character buffer.
  5288. cchDest - Size of destination buffer in characters.
  5289. Returns: Nothing.
  5290. Revision History:
  5291. Date Description Programmer
  5292. -------- --------------------------------------------------- ----------
  5293. 10/08/96 Initial creation. BrianAu
  5294. */
  5295. ///////////////////////////////////////////////////////////////////////////////
  5296. VOID
  5297. DetailsView::GetReportColHeader(
  5298. UINT iColId,
  5299. LPTSTR pszDest,
  5300. UINT cchDest
  5301. )
  5302. {
  5303. //
  5304. // WARNING: The order of these must match that of the idCol_XXX enumeration
  5305. // constants in DetailsView.
  5306. //
  5307. UINT rgTitles[] = { IDS_REPORT_HEADER_STATUS,
  5308. IDS_REPORT_HEADER_FOLDER,
  5309. IDS_REPORT_HEADER_USERNAME,
  5310. IDS_REPORT_HEADER_LOGONNAME,
  5311. IDS_REPORT_HEADER_AMTUSED,
  5312. IDS_REPORT_HEADER_LIMIT,
  5313. IDS_REPORT_HEADER_THRESHOLD,
  5314. IDS_REPORT_HEADER_PCTUSED };
  5315. DBGASSERT((NULL != pszDest));
  5316. DBGASSERT((1 < cchDest));
  5317. CString strHeader(TEXT("..."));
  5318. if (iColId < ARRAYSIZE(rgTitles))
  5319. {
  5320. strHeader.Format(g_hInstDll, rgTitles[iColId]);
  5321. }
  5322. else
  5323. {
  5324. DBGERROR((TEXT("LISTVIEW - Invalid idCol (%d) on header request"), iColId));
  5325. }
  5326. lstrcpyn(pszDest, strHeader, cchDest);
  5327. }
  5328. ///////////////////////////////////////////////////////////////////////////////
  5329. /* Function: DetailsView::GetReportRowCount
  5330. Description: Retrieves the number of data rows in the listview.
  5331. Arguments: None.
  5332. Returns: Number of rows in the listview.
  5333. Revision History:
  5334. Date Description Programmer
  5335. -------- --------------------------------------------------- ----------
  5336. 10/08/96 Initial creation. BrianAu
  5337. */
  5338. ///////////////////////////////////////////////////////////////////////////////
  5339. UINT
  5340. DetailsView::GetReportRowCount(VOID)
  5341. {
  5342. return ListView_GetSelectedCount(m_hwndListView);
  5343. }
  5344. ///////////////////////////////////////////////////////////////////////////////
  5345. /* Function: DetailsView::GetReportBinaryRecordSize
  5346. Description: Retrieves the number of bytes in a record formatted as
  5347. binary data. This should be called before GetReportBinaryRecord to
  5348. determine how to size the destination buffer.
  5349. Arguments:
  5350. iRow - 0-based index of the row in question.
  5351. Returns: Number of bytes required to store the record.
  5352. Revision History:
  5353. Date Description Programmer
  5354. -------- --------------------------------------------------- ----------
  5355. 05/22/97 Initial creation. BrianAu
  5356. */
  5357. ///////////////////////////////////////////////////////////////////////////////
  5358. UINT
  5359. DetailsView::GetReportBinaryRecordSize(
  5360. UINT iRow
  5361. )
  5362. {
  5363. INT cbRecord = 0;
  5364. PDISKQUOTA_USER pUser = NULL;
  5365. DBGASSERT((iRow < m_UserList.Count()));
  5366. if (m_UserList.Retrieve((LPVOID *)&pUser, iRow))
  5367. {
  5368. if (NULL != pUser)
  5369. {
  5370. pUser->GetSidLength((LPDWORD)&cbRecord); // Length of SID field.
  5371. cbRecord += sizeof(DWORD) + // Sid-Length field.
  5372. sizeof(LONGLONG) + // Quota used field.
  5373. sizeof(LONGLONG) + // Quota threshold field.
  5374. sizeof(LONGLONG); // Quota limit field.
  5375. }
  5376. }
  5377. return cbRecord;
  5378. }
  5379. ///////////////////////////////////////////////////////////////////////////////
  5380. /* Function: DetailsView::GetReportBinaryRecord
  5381. Description: Retrieves the information for a single row in the
  5382. details view formatted as binary data.
  5383. The format of the returned record is as follows:
  5384. +------------+---------------------------------------+
  5385. | cbSid (32) | SID (variable length) |
  5386. +------------+------------+-------------+------------+
  5387. | Quota Used (64) | Quota Threshold (64) |
  5388. +------------+------------+-------------+------------+
  5389. | Quota Limit (64) |
  5390. +------------+------------+
  5391. (*) The size of each field (bits) is shown in parentheses.
  5392. Arguments:
  5393. iRow - 0-based index of the row in question.
  5394. pbRecord - Address of destination buffer.
  5395. cbRecord - Number of bytes in destination buffer.
  5396. Returns:
  5397. TRUE = Destination buffer was sufficiently large.
  5398. FALSE = Destination buffer too small or record was invalid.
  5399. Revision History:
  5400. Date Description Programmer
  5401. -------- --------------------------------------------------- ----------
  5402. 05/22/97 Initial creation. BrianAu
  5403. */
  5404. ///////////////////////////////////////////////////////////////////////////////
  5405. BOOL
  5406. DetailsView::GetReportBinaryRecord(
  5407. UINT iRow,
  5408. LPBYTE pbRecord,
  5409. UINT cbRecord
  5410. )
  5411. {
  5412. //
  5413. // Create "PMF" (pointer to member function) as a type of pointer
  5414. // to the IDiskQuotaUser::GetQuotaXXXXXX functions. This allows us
  5415. // to build an array of function pointers and reduce the amount of
  5416. // code required.
  5417. //
  5418. typedef HRESULT(_stdcall IDiskQuotaUser::*PMF)(PLONGLONG);
  5419. PDISKQUOTA_USER pUser = NULL;
  5420. BOOL bResult = FALSE;
  5421. DBGASSERT((iRow < m_UserList.Count()));
  5422. DBGASSERT((NULL != pbRecord));
  5423. if (m_UserList.Retrieve((LPVOID *)&pUser, iRow))
  5424. {
  5425. DWORD cbSid = 0;
  5426. if (NULL != pUser && cbRecord >= sizeof(cbSid))
  5427. {
  5428. //
  5429. // Store the SID-length value first in the record.
  5430. //
  5431. pUser->GetSidLength((LPDWORD)&cbSid);
  5432. *((LPDWORD)pbRecord) = cbSid;
  5433. pbRecord += sizeof(cbSid);
  5434. cbRecord -= sizeof(cbSid);
  5435. //
  5436. // Store the SID value next.
  5437. //
  5438. if (cbRecord >= cbSid && SUCCEEDED(pUser->GetSid(pbRecord, cbRecord)))
  5439. {
  5440. pbRecord += cbSid;
  5441. cbRecord -= cbSid;
  5442. //
  5443. // An array of member function pointers. Each function
  5444. // retrieves a LONGLONG value from the quota user object.
  5445. // This places the redundant code in a loop.
  5446. //
  5447. // The value order is Quota Used, Quota Threshold, Quota Limit.
  5448. //
  5449. PMF rgpfnQuotaValue[] = {
  5450. &IDiskQuotaUser::GetQuotaUsed,
  5451. &IDiskQuotaUser::GetQuotaThreshold,
  5452. &IDiskQuotaUser::GetQuotaLimit
  5453. };
  5454. for (INT i = 0; i < ARRAYSIZE(rgpfnQuotaValue); i++)
  5455. {
  5456. bResult = TRUE;
  5457. if (cbRecord >= sizeof(LONGLONG))
  5458. {
  5459. //
  5460. // First copy to a stack LONGLONG as it is guaranteed
  5461. // to be aligned. Then byte-copy the value to the
  5462. // output buffer.
  5463. //
  5464. LONGLONG llValue;
  5465. (pUser->*(rgpfnQuotaValue[i]))(&llValue);
  5466. CopyMemory(pbRecord, &llValue, sizeof(llValue));
  5467. pbRecord += sizeof(LONGLONG);
  5468. cbRecord -= sizeof(LONGLONG);
  5469. }
  5470. else
  5471. {
  5472. //
  5473. // Insufficient buffer.
  5474. //
  5475. bResult = FALSE;
  5476. break;
  5477. }
  5478. }
  5479. }
  5480. }
  5481. }
  5482. return bResult;
  5483. }
  5484. ///////////////////////////////////////////////////////////////////////////////
  5485. /* Function: DetailsView::GiveFeedback
  5486. Description: Implementation for IDropSource::GiveFeedback.
  5487. Arguments: See IDropSource::GiveFeedback in SDK.
  5488. Returns: Always returns DRAGDROP_S_USEDEFAULTS.
  5489. We don't have any special cursors for drag/drop.
  5490. Revision History:
  5491. Date Description Programmer
  5492. -------- --------------------------------------------------- ----------
  5493. 05/20/97 Initial creation. BrianAu
  5494. */
  5495. ///////////////////////////////////////////////////////////////////////////////
  5496. HRESULT
  5497. DetailsView::GiveFeedback(
  5498. DWORD dwEffect
  5499. )
  5500. {
  5501. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DropSource::GiveFeedback")));
  5502. return DRAGDROP_S_USEDEFAULTCURSORS;
  5503. }
  5504. ///////////////////////////////////////////////////////////////////////////////
  5505. /* Function: DetailsView::QueryContinueDrag
  5506. Description: Implementation for IDropSource::QueryContinueDrag
  5507. Arguments: See IDropSource::QueryContinueDrag in SDK.
  5508. Returns:
  5509. DRAGDROP_S_CANCEL = User pressed ESC during drag.
  5510. DRAGDROP_S_DROP = User releases left mouse button.
  5511. Revision History:
  5512. Date Description Programmer
  5513. -------- --------------------------------------------------- ----------
  5514. 05/20/97 Initial creation. BrianAu
  5515. */
  5516. ///////////////////////////////////////////////////////////////////////////////
  5517. HRESULT
  5518. DetailsView::QueryContinueDrag(
  5519. BOOL fEscapePressed,
  5520. DWORD grfKeyState
  5521. )
  5522. {
  5523. HRESULT hResult = S_OK;
  5524. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DropSource::QueryContinueDrag")));
  5525. if (fEscapePressed)
  5526. hResult = DRAGDROP_S_CANCEL;
  5527. else if (!(m_DropSource.m_grfKeyState & grfKeyState))
  5528. hResult = DRAGDROP_S_DROP;
  5529. return hResult;
  5530. }
  5531. ///////////////////////////////////////////////////////////////////////////////
  5532. /* Function: DetailsView::DragEnter
  5533. Description: Implementation for IDropTarget::DragEnter
  5534. Arguments: See IDropTarget::DragEnter in SDK.
  5535. Returns: See IDropTarget::DragEnter in SDK.
  5536. Revision History:
  5537. Date Description Programmer
  5538. -------- --------------------------------------------------- ----------
  5539. 05/20/97 Initial creation. BrianAu
  5540. */
  5541. ///////////////////////////////////////////////////////////////////////////////
  5542. HRESULT
  5543. DetailsView::DragEnter(
  5544. IDataObject *pDataObject,
  5545. DWORD grfKeyState,
  5546. POINTL pt,
  5547. DWORD *pdwEffect
  5548. )
  5549. {
  5550. BOOL bWillAcceptDrop = FALSE;
  5551. HRESULT hResult = NO_ERROR;
  5552. IEnumFORMATETC *pEnum = NULL;
  5553. *pdwEffect = DROPEFFECT_NONE;
  5554. //
  5555. // Enumerate formats supported by our data object.
  5556. //
  5557. hResult = pDataObject->EnumFormatEtc(DATADIR_GET, &pEnum);
  5558. if (SUCCEEDED(hResult))
  5559. {
  5560. ULONG ulFetched = 0;
  5561. FORMATETC fmt;
  5562. //
  5563. // Search the formats until we find an acceptable match.
  5564. // We only accept our private export format along with
  5565. // CF_HDROP in stream and HGLOBAL media types.
  5566. //
  5567. while(!bWillAcceptDrop && S_OK == pEnum->Next(1, &fmt, &ulFetched))
  5568. {
  5569. if (fmt.cfFormat == DataObject::m_CF_NtDiskQuotaExport || fmt.cfFormat == CF_HDROP)
  5570. {
  5571. if (fmt.tymed & (TYMED_HGLOBAL | TYMED_ISTREAM))
  5572. {
  5573. bWillAcceptDrop = TRUE;
  5574. }
  5575. }
  5576. }
  5577. pEnum->Release();
  5578. }
  5579. if (SUCCEEDED(hResult))
  5580. {
  5581. hResult = NO_ERROR;
  5582. if (bWillAcceptDrop)
  5583. {
  5584. *pdwEffect = (grfKeyState & MK_CONTROL ? DROPEFFECT_COPY :
  5585. DROPEFFECT_MOVE);
  5586. m_DropTarget.m_pIDataObject = pDataObject;
  5587. m_DropTarget.m_pIDataObject->AddRef();
  5588. }
  5589. }
  5590. return hResult;
  5591. }
  5592. ///////////////////////////////////////////////////////////////////////////////
  5593. /* Function: DetailsView::DragOver
  5594. Description: Implementation for IDropTarget::DragOver
  5595. Arguments: See IDropTarget::DragOver in SDK.
  5596. Returns: See IDropTarget::DragOver in SDK.
  5597. Revision History:
  5598. Date Description Programmer
  5599. -------- --------------------------------------------------- ----------
  5600. 05/20/97 Initial creation. BrianAu
  5601. */
  5602. ///////////////////////////////////////////////////////////////////////////////
  5603. HRESULT
  5604. DetailsView::DragOver(
  5605. DWORD grfKeyState,
  5606. POINTL pt,
  5607. DWORD *pdwEffect
  5608. )
  5609. {
  5610. if (NULL != m_DropTarget.m_pIDataObject)
  5611. {
  5612. *pdwEffect = (grfKeyState & MK_CONTROL ? DROPEFFECT_COPY :
  5613. DROPEFFECT_MOVE);
  5614. }
  5615. else
  5616. {
  5617. *pdwEffect = DROPEFFECT_NONE;
  5618. }
  5619. return NO_ERROR;
  5620. }
  5621. ///////////////////////////////////////////////////////////////////////////////
  5622. /* Function: DetailsView::DragLeave
  5623. Description: Implementation for IDropTarget::DragLeave
  5624. Arguments: See IDropTarget::DragLeave in SDK.
  5625. Returns: See IDropTarget::DragLeave in SDK.
  5626. Revision History:
  5627. Date Description Programmer
  5628. -------- --------------------------------------------------- ----------
  5629. 05/20/97 Initial creation. BrianAu
  5630. */
  5631. ///////////////////////////////////////////////////////////////////////////////
  5632. HRESULT
  5633. DetailsView::DragLeave(
  5634. VOID
  5635. )
  5636. {
  5637. if (NULL != m_DropTarget.m_pIDataObject)
  5638. {
  5639. m_DropTarget.m_pIDataObject->Release();
  5640. }
  5641. return NO_ERROR;
  5642. }
  5643. ///////////////////////////////////////////////////////////////////////////////
  5644. /* Function: DetailsView::Drop
  5645. Description: Implementation for IDropTarget::Drop
  5646. Arguments: See IDropTarget::Drop in SDK.
  5647. Returns: See IDropTarget::Drop in SDK.
  5648. Revision History:
  5649. Date Description Programmer
  5650. -------- --------------------------------------------------- ----------
  5651. 05/20/97 Initial creation. BrianAu
  5652. */
  5653. ///////////////////////////////////////////////////////////////////////////////
  5654. HRESULT
  5655. DetailsView::Drop(
  5656. IDataObject *pDataObject,
  5657. DWORD grfKeyState,
  5658. POINTL pt,
  5659. DWORD *pdwEffect
  5660. )
  5661. {
  5662. HRESULT hResult = E_FAIL;
  5663. *pdwEffect = DROPEFFECT_NONE;
  5664. if (NULL != m_DropTarget.m_pIDataObject)
  5665. {
  5666. DragLeave();
  5667. //
  5668. // Import the quota data from the data object.
  5669. //
  5670. Importer importer(*this);
  5671. hResult = importer.Import(pDataObject);
  5672. if (SUCCEEDED(hResult))
  5673. {
  5674. if (grfKeyState & MK_CONTROL)
  5675. {
  5676. *pdwEffect = DROPEFFECT_COPY;
  5677. }
  5678. }
  5679. }
  5680. return hResult;
  5681. }
  5682. ///////////////////////////////////////////////////////////////////////////////
  5683. /* Function: DetailsView::GetData
  5684. Description: Implementation of IDataObject::GetData
  5685. Arguments: See IDataObject::GetData in SDK.
  5686. Returns: See IDataObject::GetData in SDK.
  5687. Revision History:
  5688. Date Description Programmer
  5689. -------- --------------------------------------------------- ----------
  5690. 05/20/97 Initial creation. BrianAu
  5691. */
  5692. ///////////////////////////////////////////////////////////////////////////////
  5693. STDMETHODIMP
  5694. DetailsView::GetData(
  5695. FORMATETC *pFormatEtc,
  5696. STGMEDIUM *pMedium
  5697. )
  5698. {
  5699. HRESULT hResult = E_INVALIDARG;
  5700. #if DBG
  5701. TCHAR szCFName[MAX_PATH] = { TEXT('\0') };
  5702. GetClipboardFormatName(pFormatEtc->cfFormat, szCFName, ARRAYSIZE(szCFName));
  5703. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::GetData\nDVA = %d CF = %d (%s) tymed = %d"),
  5704. pFormatEtc->dwAspect, pFormatEtc->cfFormat,
  5705. szCFName,
  5706. pFormatEtc->tymed));
  5707. #endif // DEBUG
  5708. if (NULL != pFormatEtc && NULL != pMedium)
  5709. {
  5710. //
  5711. // See if we support the requested format.
  5712. //
  5713. hResult = m_pDataObject->IsFormatSupported(pFormatEtc);
  5714. if (SUCCEEDED(hResult))
  5715. {
  5716. //
  5717. // Yep, we support it. Render the data.
  5718. //
  5719. hResult = m_pDataObject->RenderData(pFormatEtc, pMedium);
  5720. }
  5721. }
  5722. return hResult;
  5723. }
  5724. ///////////////////////////////////////////////////////////////////////////////
  5725. /* Function: DetailsView::GetDataHere
  5726. Description: Implementation of IDataObject::GetDataHere
  5727. Arguments: See IDataObject::GetData in SDK.
  5728. Returns: E_NOTIMPL
  5729. Revision History:
  5730. Date Description Programmer
  5731. -------- --------------------------------------------------- ----------
  5732. 05/20/97 Initial creation. BrianAu
  5733. */
  5734. ///////////////////////////////////////////////////////////////////////////////
  5735. STDMETHODIMP
  5736. DetailsView::GetDataHere(
  5737. FORMATETC *pFormatEtc,
  5738. STGMEDIUM *pMedium
  5739. )
  5740. {
  5741. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::GetDataHere")));
  5742. return E_NOTIMPL;
  5743. }
  5744. ///////////////////////////////////////////////////////////////////////////////
  5745. /* Function: DetailsView::QueryGetData
  5746. Description: Implementation of IDataObject::QueryGetData
  5747. Arguments: See IDataObject::QueryGetData in SDK.
  5748. Returns: See IDataObject::QueryGetData in SDK.
  5749. Revision History:
  5750. Date Description Programmer
  5751. -------- --------------------------------------------------- ----------
  5752. 05/20/97 Initial creation. BrianAu
  5753. */
  5754. ///////////////////////////////////////////////////////////////////////////////
  5755. STDMETHODIMP
  5756. DetailsView::QueryGetData(
  5757. FORMATETC *pFormatEtc
  5758. )
  5759. {
  5760. HRESULT hResult = E_UNEXPECTED;
  5761. #if DBG
  5762. TCHAR szCFName[MAX_PATH] = { TEXT('\0') };
  5763. GetClipboardFormatName(pFormatEtc->cfFormat, szCFName, ARRAYSIZE(szCFName));
  5764. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::QueryGetData\nDVA = %d CF = %d (%s) tymed = %d"),
  5765. pFormatEtc->dwAspect, pFormatEtc->cfFormat,
  5766. szCFName,
  5767. pFormatEtc->tymed));
  5768. #endif // DEBUG
  5769. if (NULL != pFormatEtc)
  5770. {
  5771. hResult = m_pDataObject->IsFormatSupported(pFormatEtc);
  5772. }
  5773. else
  5774. hResult = E_INVALIDARG;
  5775. return hResult;
  5776. }
  5777. ///////////////////////////////////////////////////////////////////////////////
  5778. /* Function: DetailsView::GetCanonicalFormatEtc
  5779. Description: Implementation of IDataObject::GetCanonicalFormatEtc
  5780. Arguments: See IDataObject::GetCanonicalFormatEtc in SDK.
  5781. Returns: See IDataObject::GetCanonicalFormatEtc in SDK.
  5782. Revision History:
  5783. Date Description Programmer
  5784. -------- --------------------------------------------------- ----------
  5785. 05/20/97 Initial creation. BrianAu
  5786. */
  5787. ///////////////////////////////////////////////////////////////////////////////
  5788. STDMETHODIMP
  5789. DetailsView::GetCanonicalFormatEtc(
  5790. FORMATETC *pFormatEtcIn,
  5791. FORMATETC *pFormatEtcOut
  5792. )
  5793. {
  5794. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::GetCanonicalFormatEtc")));
  5795. HRESULT hResult = E_INVALIDARG;
  5796. if (NULL != pFormatEtcIn && NULL != pFormatEtcOut)
  5797. {
  5798. CopyMemory(pFormatEtcOut, pFormatEtcIn, sizeof(*pFormatEtcOut));
  5799. pFormatEtcOut->ptd = NULL;
  5800. hResult = DATA_S_SAMEFORMATETC;
  5801. }
  5802. return hResult;
  5803. }
  5804. ///////////////////////////////////////////////////////////////////////////////
  5805. /* Function: DetailsView::SetData
  5806. Description: Implementation of IDataObject::SetData
  5807. Arguments: See IDataObject::SetData in SDK.
  5808. Returns: E_NOTIMPL.
  5809. Revision History:
  5810. Date Description Programmer
  5811. -------- --------------------------------------------------- ----------
  5812. 05/20/97 Initial creation. BrianAu
  5813. */
  5814. ///////////////////////////////////////////////////////////////////////////////
  5815. STDMETHODIMP
  5816. DetailsView::SetData(
  5817. FORMATETC *pFormatEtc,
  5818. STGMEDIUM *pMedium,
  5819. BOOL fRelease
  5820. )
  5821. {
  5822. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::SetData")));
  5823. return E_NOTIMPL;
  5824. }
  5825. ///////////////////////////////////////////////////////////////////////////////
  5826. /* Function: DetailsView::EnumFormatEtc
  5827. Description: Implementation of IDataObject::EnumFormatEtc
  5828. Arguments: See IDataObject::EnumFormatEtc in SDK.
  5829. Returns: See IDataObject::GetCanonicalFormatEtc in SDK.
  5830. Revision History:
  5831. Date Description Programmer
  5832. -------- --------------------------------------------------- ----------
  5833. 05/20/97 Initial creation. BrianAu
  5834. */
  5835. ///////////////////////////////////////////////////////////////////////////////
  5836. STDMETHODIMP
  5837. DetailsView::EnumFormatEtc(
  5838. DWORD dwDirection,
  5839. IEnumFORMATETC **ppenumFormatEtc
  5840. )
  5841. {
  5842. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::EnumFormatEtc")));
  5843. HRESULT hResult = E_FAIL;
  5844. EnumFORMATETC *pEnum = NULL;
  5845. switch(dwDirection)
  5846. {
  5847. case DATADIR_GET:
  5848. try
  5849. {
  5850. pEnum = new EnumFORMATETC(m_pDataObject->m_cFormats, m_pDataObject->m_rgFormats);
  5851. hResult = pEnum->QueryInterface(IID_IEnumFORMATETC, (LPVOID *)ppenumFormatEtc);
  5852. }
  5853. catch(CAllocException& e)
  5854. {
  5855. *ppenumFormatEtc = NULL;
  5856. hResult = E_OUTOFMEMORY;
  5857. }
  5858. break;
  5859. case DATADIR_SET:
  5860. //
  5861. // SetData not implemented.
  5862. //
  5863. default:
  5864. *ppenumFormatEtc = NULL;
  5865. break;
  5866. }
  5867. return hResult;
  5868. }
  5869. ///////////////////////////////////////////////////////////////////////////////
  5870. /* Function: DetailsView::DAdvise
  5871. Description: Implementation of IDataObject::DAdvise
  5872. Arguments: See IDataObject::DAdvise in SDK.
  5873. Returns: E_NOTIMPL
  5874. Revision History:
  5875. Date Description Programmer
  5876. -------- --------------------------------------------------- ----------
  5877. 05/20/97 Initial creation. BrianAu
  5878. */
  5879. ///////////////////////////////////////////////////////////////////////////////
  5880. STDMETHODIMP
  5881. DetailsView::DAdvise(
  5882. FORMATETC *pFormatEtc,
  5883. DWORD advf,
  5884. IAdviseSink *pAdvSink,
  5885. DWORD *pdwConnection
  5886. )
  5887. {
  5888. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::DAdvise")));
  5889. return E_NOTIMPL;
  5890. }
  5891. ///////////////////////////////////////////////////////////////////////////////
  5892. /* Function: DetailsView::DUnadvise
  5893. Description: Implementation of IDataObject::DUnadvise
  5894. Arguments: See IDataObject::DUnadvise in SDK.
  5895. Returns: E_NOTIMPL
  5896. Revision History:
  5897. Date Description Programmer
  5898. -------- --------------------------------------------------- ----------
  5899. 05/20/97 Initial creation. BrianAu
  5900. */
  5901. ///////////////////////////////////////////////////////////////////////////////
  5902. STDMETHODIMP
  5903. DetailsView::DUnadvise(
  5904. DWORD dwConnection
  5905. )
  5906. {
  5907. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::DUnadvise")));
  5908. return E_NOTIMPL;
  5909. }
  5910. ///////////////////////////////////////////////////////////////////////////////
  5911. /* Function: DetailsView::EnumDAdvise
  5912. Description: Implementation of IDataObject::EnumDAdvise
  5913. Arguments: See IDataObject::EnumDAdvise in SDK.
  5914. Returns: E_NOTIMPL
  5915. Revision History:
  5916. Date Description Programmer
  5917. -------- --------------------------------------------------- ----------
  5918. 05/20/97 Initial creation. BrianAu
  5919. */
  5920. ///////////////////////////////////////////////////////////////////////////////
  5921. STDMETHODIMP
  5922. DetailsView::EnumDAdvise(
  5923. IEnumSTATDATA **ppenumAdvise
  5924. )
  5925. {
  5926. DBGTRACE((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - DataObject::EnumDAdvise")));
  5927. return E_NOTIMPL;
  5928. }
  5929. //
  5930. // Number of clipboard formats supported by our data object.
  5931. // Change this if you add/remove clipboard formats. There's an assert
  5932. // in the DataObject ctor to ensure this.
  5933. //
  5934. const INT DetailsView::DataObject::CF_FORMATS_SUPPORTED = 14;
  5935. //
  5936. // Name of data stream in import/export and dragdrop streams.
  5937. //
  5938. LPCWSTR DetailsView::DataObject::SZ_EXPORT_STREAM_NAME = L"NT DISKQUOTA IMPORTEXPORT";
  5939. LPCTSTR DetailsView::DataObject::SZ_EXPORT_CF_NAME = TEXT("NT DISKQUTOA IMPORTEXPORT");
  5940. //
  5941. // The version of export data produced by this module. This value
  5942. // is written into the stream immediately following the GUID. If the
  5943. // format of the export stream is changed, this value should be incremented.
  5944. //
  5945. const DWORD DetailsView::DataObject::EXPORT_STREAM_VERSION = 1;
  5946. CLIPFORMAT DetailsView::DataObject::m_CF_Csv = 0; // Comma-separated fields format.
  5947. CLIPFORMAT DetailsView::DataObject::m_CF_RichText = 0; // RTF format.
  5948. CLIPFORMAT DetailsView::DataObject::m_CF_NtDiskQuotaExport = 0; // Internal fmt for import/export.
  5949. CLIPFORMAT DetailsView::DataObject::m_CF_FileGroupDescriptor = 0;
  5950. CLIPFORMAT DetailsView::DataObject::m_CF_FileContents = 0;
  5951. ///////////////////////////////////////////////////////////////////////////////
  5952. /* Function: DetailsView::DataObject::DataObject
  5953. Description: Constructor for implementation of IDataObject.
  5954. Arguments:
  5955. DV - Reference to details view object that contains the data object.
  5956. Returns: Nothing
  5957. Revision History:
  5958. Date Description Programmer
  5959. -------- --------------------------------------------------- ----------
  5960. 05/20/97 Initial creation. BrianAu
  5961. */
  5962. ///////////////////////////////////////////////////////////////////////////////
  5963. DetailsView::DataObject::DataObject(
  5964. DetailsView& DV
  5965. ) : m_pStg(NULL),
  5966. m_pStm(NULL),
  5967. m_rgFormats(NULL),
  5968. m_cFormats(CF_FORMATS_SUPPORTED),
  5969. m_DV(DV)
  5970. {
  5971. DBGTRACE((DM_VIEW, DL_HIGH, TEXT("DetailsView::DataObject::DataObject")));
  5972. DBGPRINT((DM_VIEW, DL_HIGH, TEXT("\tthis = 0x%08X"), this));
  5973. //
  5974. // Get additional clipboard formats we support.
  5975. //
  5976. if (0 == m_CF_Csv)
  5977. {
  5978. m_CF_Csv = (CLIPFORMAT)RegisterClipboardFormat(TEXT("Csv"));
  5979. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - Csv CF = %d"), m_CF_Csv));
  5980. }
  5981. if (0 == m_CF_RichText)
  5982. {
  5983. m_CF_RichText = (CLIPFORMAT)RegisterClipboardFormat(TEXT("Rich Text Format"));
  5984. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - RTF CF = %d"), m_CF_RichText));
  5985. }
  5986. if (0 == m_CF_NtDiskQuotaExport)
  5987. {
  5988. m_CF_NtDiskQuotaExport = (CLIPFORMAT)RegisterClipboardFormat(DataObject::SZ_EXPORT_CF_NAME);
  5989. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - NtDiskQuotaExport = %d"), m_CF_NtDiskQuotaExport));
  5990. }
  5991. if (0 == m_CF_FileGroupDescriptor)
  5992. {
  5993. m_CF_FileGroupDescriptor = (CLIPFORMAT)RegisterClipboardFormat(TEXT("FileGroupDescriptorW"));
  5994. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - FileGroupDescriptorW = %d"), m_CF_FileGroupDescriptor));
  5995. }
  5996. if (0 == m_CF_FileContents)
  5997. {
  5998. m_CF_FileContents = (CLIPFORMAT)RegisterClipboardFormat(TEXT("FileContents"));
  5999. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - FileContents = %d"), m_CF_FileContents));
  6000. }
  6001. //
  6002. // Create the array to hold the FORMATETC structures that describe the
  6003. // formats we support.
  6004. //
  6005. m_rgFormats = new FORMATETC[m_cFormats];
  6006. //
  6007. // Specify all formats and media we support.
  6008. // Place the richest formats first in the array.
  6009. // These are used to initialize the format enumerator when it
  6010. // is requested.
  6011. //
  6012. UINT iFmt = 0;
  6013. SetFormatEtc(m_rgFormats[iFmt++], m_CF_FileGroupDescriptor, TYMED_ISTREAM);
  6014. SetFormatEtc(m_rgFormats[iFmt++], m_CF_FileGroupDescriptor, TYMED_HGLOBAL);
  6015. SetFormatEtc(m_rgFormats[iFmt++], m_CF_FileContents, TYMED_ISTREAM);
  6016. SetFormatEtc(m_rgFormats[iFmt++], m_CF_FileContents, TYMED_HGLOBAL);
  6017. SetFormatEtc(m_rgFormats[iFmt++], m_CF_NtDiskQuotaExport, TYMED_ISTREAM);
  6018. SetFormatEtc(m_rgFormats[iFmt++], m_CF_NtDiskQuotaExport, TYMED_HGLOBAL);
  6019. SetFormatEtc(m_rgFormats[iFmt++], m_CF_RichText, TYMED_ISTREAM);
  6020. SetFormatEtc(m_rgFormats[iFmt++], m_CF_RichText, TYMED_HGLOBAL);
  6021. SetFormatEtc(m_rgFormats[iFmt++], m_CF_Csv, TYMED_ISTREAM);
  6022. SetFormatEtc(m_rgFormats[iFmt++], m_CF_Csv, TYMED_HGLOBAL);
  6023. SetFormatEtc(m_rgFormats[iFmt++], CF_UNICODETEXT, TYMED_ISTREAM);
  6024. SetFormatEtc(m_rgFormats[iFmt++], CF_UNICODETEXT, TYMED_HGLOBAL);
  6025. SetFormatEtc(m_rgFormats[iFmt++], CF_TEXT, TYMED_ISTREAM);
  6026. SetFormatEtc(m_rgFormats[iFmt++], CF_TEXT, TYMED_HGLOBAL);
  6027. //
  6028. // If you hit this, you need to adjust CF_FORMATS_SUPPORTED to match
  6029. // the number of SetFormatEtc statements above.
  6030. // Otherwise, you just overwrote the m_rgFormats[] allocation.
  6031. //
  6032. DBGASSERT((iFmt == m_cFormats));
  6033. }
  6034. ///////////////////////////////////////////////////////////////////////////////
  6035. /* Function: DetailsView::DataObject::~DataObject
  6036. Description: Destructor for implementation of IDataObject.
  6037. Arguments: None.
  6038. Returns: Nothing
  6039. Revision History:
  6040. Date Description Programmer
  6041. -------- --------------------------------------------------- ----------
  6042. 05/20/97 Initial creation. BrianAu
  6043. */
  6044. ///////////////////////////////////////////////////////////////////////////////
  6045. DetailsView::DataObject::~DataObject(
  6046. VOID
  6047. )
  6048. {
  6049. DBGTRACE((DM_VIEW, DL_HIGH, TEXT("DetailsView::DataObject::~DataObject")));
  6050. DBGPRINT((DM_VIEW, DL_HIGH, TEXT("\tthis = 0x%08X"), this));
  6051. delete[] m_rgFormats;
  6052. if (NULL != m_pStg)
  6053. m_pStg->Release();
  6054. //
  6055. // NOTE: m_pStm is released by the data object's recipient
  6056. // through ReleaseStgMedium.
  6057. //
  6058. }
  6059. ///////////////////////////////////////////////////////////////////////////////
  6060. /* Function: DetailsView::DataObject::IsFormatSupported
  6061. Description: Determines if a given format is supported by our implementation.
  6062. Arguments:
  6063. pFormatEtc - Address of FORMATETC structure containing request info.
  6064. Returns:
  6065. NO_ERROR - Supported.
  6066. DV_E_TYMED - Medium type not supported.
  6067. DV_E_FORMATETC - Clipboard format not supported.
  6068. DV_E_DVASPECT - Device aspect not supported.
  6069. Revision History:
  6070. Date Description Programmer
  6071. -------- --------------------------------------------------- ----------
  6072. 10/10/96 Initial creation. BrianAu
  6073. */
  6074. ///////////////////////////////////////////////////////////////////////////////
  6075. HRESULT
  6076. DetailsView::DataObject::IsFormatSupported(
  6077. FORMATETC *pFormatEtc
  6078. )
  6079. {
  6080. DBGASSERT((NULL != pFormatEtc));
  6081. HRESULT hResult = E_FAIL;
  6082. if (DVASPECT_CONTENT == pFormatEtc->dwAspect)
  6083. {
  6084. if (CF_TEXT == pFormatEtc->cfFormat ||
  6085. CF_UNICODETEXT == pFormatEtc->cfFormat ||
  6086. m_CF_RichText == pFormatEtc->cfFormat ||
  6087. m_CF_Csv == pFormatEtc->cfFormat ||
  6088. m_CF_NtDiskQuotaExport == pFormatEtc->cfFormat ||
  6089. m_CF_FileGroupDescriptor == pFormatEtc->cfFormat ||
  6090. m_CF_FileContents == pFormatEtc->cfFormat)
  6091. {
  6092. if (pFormatEtc->tymed & (TYMED_ISTREAM | TYMED_HGLOBAL))
  6093. {
  6094. hResult = NO_ERROR;
  6095. }
  6096. else
  6097. {
  6098. hResult = DV_E_TYMED;
  6099. }
  6100. }
  6101. else
  6102. hResult = DV_E_FORMATETC;
  6103. }
  6104. else
  6105. hResult = DV_E_DVASPECT;
  6106. return hResult;
  6107. }
  6108. ///////////////////////////////////////////////////////////////////////////////
  6109. /* Function: DetailsView::DataObject::CreateRenderStream
  6110. Description: Creates the OLE stream on which the data is to be rendered.
  6111. Arguments:
  6112. tymed - Desired medium type.
  6113. ppStm - Address of IStream pointer variable to receive the stream ptr.
  6114. Returns:
  6115. NO_ERROR - Success.
  6116. E_INVALIDARG - Invalid medium type.
  6117. E_OUTOFMEMORY - Insufficient memory.
  6118. Revision History:
  6119. Date Description Programmer
  6120. -------- --------------------------------------------------- ----------
  6121. 07/30/97 Initial creation. BrianAu
  6122. */
  6123. ///////////////////////////////////////////////////////////////////////////////
  6124. HRESULT
  6125. DetailsView::DataObject::CreateRenderStream(
  6126. DWORD tymed,
  6127. IStream **ppStm
  6128. )
  6129. {
  6130. HRESULT hResult = NOERROR;
  6131. //
  6132. // Create the Stream.
  6133. //
  6134. if (TYMED_ISTREAM & tymed)
  6135. {
  6136. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - CreateRenderStream for ISTREAM")));
  6137. hResult = CreateStreamOnHGlobal(NULL, // Block of 0 bytes.
  6138. TRUE, // Delete on release.
  6139. ppStm);
  6140. }
  6141. else if (TYMED_HGLOBAL & tymed)
  6142. {
  6143. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DRAGDROP - CreateRenderStream for HGLOBAL")));
  6144. hResult = CreateStreamOnHGlobal(NULL, // Block of 0 bytes.
  6145. TRUE, // Delete on release.
  6146. ppStm);
  6147. }
  6148. return hResult;
  6149. }
  6150. ///////////////////////////////////////////////////////////////////////////////
  6151. /* Function: DetailsView::DataObject::RenderData [private]
  6152. Description: Renders the data in the Details View onto the provided
  6153. stream using the requested clipboard format.
  6154. Arguments:
  6155. pStm - Pointer to output stream.
  6156. cf - Desired clipboard format.
  6157. Returns:
  6158. NO_ERROR - Success.
  6159. E_FAIL - General failure.
  6160. STG_E_WRITEFAULT - Media write error.
  6161. STG_E_MEDIUMFULL - Insufficient space on medium.
  6162. E_ACCESSDENIED - Write access denied.
  6163. E_OUTOFMEMORY - Insufficient memory.
  6164. E_UNEXPECTED - Unexpected exception.
  6165. Revision History:
  6166. Date Description Programmer
  6167. -------- --------------------------------------------------- ----------
  6168. 07/30/97 Initial creation. BrianAu
  6169. */
  6170. ///////////////////////////////////////////////////////////////////////////////
  6171. HRESULT
  6172. DetailsView::DataObject::RenderData(
  6173. IStream *pStm,
  6174. CLIPFORMAT cf
  6175. )
  6176. {
  6177. HRESULT hResult = NOERROR;
  6178. Renderer *pRenderer = NULL;
  6179. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("DetailsView::DataObject::RenderData on stream")));
  6180. try
  6181. {
  6182. //
  6183. // Create the properly-typed rendering object for the requested format.
  6184. //
  6185. switch(cf)
  6186. {
  6187. case CF_TEXT:
  6188. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is CF_TEXT")));
  6189. pRenderer = new Renderer_TEXT(m_DV);
  6190. break;
  6191. case CF_UNICODETEXT:
  6192. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is CF_UNICODETEXT")));
  6193. pRenderer = new Renderer_UNICODETEXT(m_DV);
  6194. break;
  6195. default:
  6196. if (m_CF_RichText == cf)
  6197. {
  6198. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is RTF")));
  6199. pRenderer = new Renderer_RTF(m_DV);
  6200. }
  6201. else if (m_CF_Csv == cf)
  6202. {
  6203. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is Csv")));
  6204. pRenderer = new Renderer_Csv(m_DV);
  6205. }
  6206. else if (m_CF_NtDiskQuotaExport == cf)
  6207. {
  6208. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is Windows NT Disk Quota ImportExport Format")));
  6209. pRenderer = new Renderer_Export(m_DV);
  6210. }
  6211. else if (m_CF_FileGroupDescriptor == cf)
  6212. {
  6213. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is FileGroupDescriptor")));
  6214. pRenderer = new Renderer_FileGroupDescriptor(m_DV);
  6215. }
  6216. else if (m_CF_FileContents == cf)
  6217. {
  6218. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Format is FileContents")));
  6219. pRenderer = new Renderer_FileContents(m_DV);
  6220. }
  6221. else
  6222. {
  6223. DBGPRINT((DM_DRAGDROP, DL_MID, TEXT("Unknown CF format (%d) requested"), cf));
  6224. }
  6225. break;
  6226. }
  6227. if (NULL != pRenderer)
  6228. {
  6229. m_pStm->AddRef(); // Giving stream to renderer.
  6230. // Will be released when renderer is destroyed.
  6231. //
  6232. // Render the information onto the stream.
  6233. // This can throw FileError exceptions if we run out of disk
  6234. // space or there's a disk write error.
  6235. //
  6236. pRenderer->Render(m_pStm);
  6237. }
  6238. }
  6239. catch(CFileException& fe)
  6240. {
  6241. switch(fe.Reason())
  6242. {
  6243. case CFileException::write:
  6244. DBGERROR((TEXT("FileWrite error")));
  6245. hResult = E_FAIL;
  6246. break;
  6247. case CFileException::device:
  6248. DBGERROR((TEXT("Disk error")));
  6249. hResult = STG_E_WRITEFAULT;
  6250. break;
  6251. case CFileException::diskfull:
  6252. DBGERROR((TEXT("Disk Full error")));
  6253. hResult = STG_E_MEDIUMFULL;
  6254. break;
  6255. case CFileException::access:
  6256. DBGERROR((TEXT("Access Denied error")));
  6257. hResult = E_ACCESSDENIED;
  6258. break;
  6259. default:
  6260. DBGERROR((TEXT("Other error")));
  6261. hResult = E_FAIL;
  6262. break;
  6263. }
  6264. }
  6265. catch(CAllocException& e)
  6266. {
  6267. DBGERROR((TEXT("Insufficient memory")));
  6268. hResult = E_OUTOFMEMORY;
  6269. }
  6270. delete pRenderer;
  6271. return hResult;
  6272. }
  6273. ///////////////////////////////////////////////////////////////////////////////
  6274. /* Function: DetailsView::DataObject::RenderData [public]
  6275. Description: Renders the data in the Details View onto the requested
  6276. medium using the requested format.
  6277. Arguments:
  6278. pFormatEtc - Address of FORMATETC structure containing request info.
  6279. pMedium - Address of STGMEDIUM structure containing requested
  6280. medium info.
  6281. Returns:
  6282. NO_ERROR - Success.
  6283. Can return many other OLE drag/drop error codes.
  6284. Revision History:
  6285. Date Description Programmer
  6286. -------- --------------------------------------------------- ----------
  6287. 10/10/96 Initial creation. BrianAu
  6288. 07/30/97 Reworked. Moved some code into CreateRenderStream BrianAu
  6289. and CreateAndRunRenderer. Makes the function
  6290. more understandable.
  6291. */
  6292. ///////////////////////////////////////////////////////////////////////////////
  6293. HRESULT
  6294. DetailsView::DataObject::RenderData(
  6295. FORMATETC *pFormatEtc,
  6296. STGMEDIUM *pMedium
  6297. )
  6298. {
  6299. DBGASSERT((NULL != pFormatEtc));
  6300. DBGASSERT((NULL != pMedium));
  6301. DBGASSERT((SUCCEEDED(IsFormatSupported(pFormatEtc))));
  6302. HRESULT hResult = NOERROR;
  6303. Renderer *pRenderer = NULL;
  6304. DBGPRINT((DM_DRAGDROP, DL_HIGH, TEXT("DetailsView::DataObject::RenderData on medium")));
  6305. //
  6306. // Create the stream we'll render the data onto.
  6307. //
  6308. hResult = CreateRenderStream(pFormatEtc->tymed, &m_pStm);
  6309. if (SUCCEEDED(hResult))
  6310. {
  6311. //
  6312. // Render the data on the stream.
  6313. //
  6314. hResult = RenderData(m_pStm, pFormatEtc->cfFormat);
  6315. if (SUCCEEDED(hResult))
  6316. {
  6317. //
  6318. // If we've made it here, we have a valid drag-drop report on m_pStm.
  6319. // Now set up the stg medium to transfer the rendering.
  6320. //
  6321. if (TYMED_ISTREAM & pFormatEtc->tymed)
  6322. {
  6323. pMedium->pstm = m_pStm;
  6324. pMedium->tymed = TYMED_ISTREAM;
  6325. pMedium->pUnkForRelease = NULL; // Target will free the Stream.
  6326. }
  6327. else if (TYMED_HGLOBAL & pFormatEtc->tymed)
  6328. {
  6329. pMedium->tymed = TYMED_HGLOBAL;
  6330. pMedium->pUnkForRelease = NULL; // Target will free the mem.
  6331. hResult = GetHGlobalFromStream(m_pStm,
  6332. &pMedium->hGlobal);
  6333. }
  6334. else
  6335. {
  6336. //
  6337. // Call to CreateRenderStream() should have failed if we
  6338. // hit this.
  6339. //
  6340. DBGASSERT((0));
  6341. }
  6342. }
  6343. if (FAILED(hResult))
  6344. {
  6345. DBGERROR((TEXT("DRAGDROP - Error 0x%08X rendering data"), hResult));
  6346. //
  6347. // Something failed after the stream was created.
  6348. // The DetailsView::DataObject dtor DOES NOT release it.
  6349. // It assumes success and assumes the recipient will release it.
  6350. // Release the stream.
  6351. //
  6352. m_pStm->Release();
  6353. //
  6354. // These two statements are redundant since pMedium contains a union.
  6355. // I didn't want any more if(STREAM) else if (HGLOBAL) logic. In case
  6356. // there's ever a change in structure, this will ensure both possible
  6357. // medium types are null'd out.
  6358. //
  6359. pMedium->pstm = NULL;
  6360. pMedium->hGlobal = NULL;
  6361. }
  6362. }
  6363. else
  6364. {
  6365. DBGERROR((TEXT("DRAGDROP - Error 0x%08X creating stream"), hResult);)
  6366. }
  6367. return hResult;
  6368. }
  6369. ///////////////////////////////////////////////////////////////////////////////
  6370. /* Function: DetailsView::DataObject::SetFormatEtc [static]
  6371. Description: Helper function to set the members of a FORMATETC
  6372. structure. Uses defaults for least used members.
  6373. Arguments: See SDK description of FORMATETC.
  6374. Returns: Nothing.
  6375. Revision History:
  6376. Date Description Programmer
  6377. -------- --------------------------------------------------- ----------
  6378. 05/20/97 Initial creation. BrianAu
  6379. */
  6380. ///////////////////////////////////////////////////////////////////////////////
  6381. VOID
  6382. DetailsView::DataObject::SetFormatEtc(
  6383. FORMATETC& fe,
  6384. CLIPFORMAT cfFormat,
  6385. DWORD tymed,
  6386. DWORD dwAspect,
  6387. DVTARGETDEVICE *ptd,
  6388. LONG lindex
  6389. )
  6390. {
  6391. fe.cfFormat = cfFormat;
  6392. fe.dwAspect = dwAspect;
  6393. fe.ptd = ptd;
  6394. fe.tymed = tymed;
  6395. fe.lindex = lindex;
  6396. };
  6397. ///////////////////////////////////////////////////////////////////////////////
  6398. /* Function: DetailsView::DataObject::WideToAnsi
  6399. Description: Helper function to convert a wide character string to ANSI.
  6400. The caller must delete the return buffer.
  6401. Arguments:
  6402. pszTextW - UNICODE string to convert.
  6403. Returns: Address of ANSI string. Caller must delete this.
  6404. Revision History:
  6405. Date Description Programmer
  6406. -------- --------------------------------------------------- ----------
  6407. 05/20/97 Initial creation. BrianAu
  6408. */
  6409. ///////////////////////////////////////////////////////////////////////////////
  6410. LPSTR
  6411. DetailsView::DataObject::WideToAnsi(
  6412. LPCWSTR pszTextW
  6413. )
  6414. {
  6415. DBGASSERT((NULL != pszTextW));
  6416. INT cchTextA = WideCharToMultiByte(CP_ACP,
  6417. 0,
  6418. pszTextW,
  6419. -1,
  6420. NULL,
  6421. 0,
  6422. NULL,
  6423. NULL);
  6424. LPSTR pszTextA = new CHAR[cchTextA + 1];
  6425. WideCharToMultiByte(CP_ACP,
  6426. 0,
  6427. pszTextW,
  6428. -1,
  6429. pszTextA,
  6430. cchTextA + 1,
  6431. NULL,
  6432. NULL);
  6433. return pszTextA;
  6434. }
  6435. ///////////////////////////////////////////////////////////////////////////////
  6436. /* Function: DetailsView::DataObject::Renderer::Render
  6437. Description: Render the selected items in the listview on a stream.
  6438. Calls virtual functions defined by derived classes to produce the
  6439. required format.
  6440. Arguments:
  6441. pStm - Address of IStream on which to write output.
  6442. Assumes that this pointer has been AddRef'd by the caller.
  6443. Returns: Nothing.
  6444. Revision History:
  6445. Date Description Programmer
  6446. -------- --------------------------------------------------- ----------
  6447. 05/20/97 Initial creation. BrianAu
  6448. */
  6449. ///////////////////////////////////////////////////////////////////////////////
  6450. VOID
  6451. DetailsView::DataObject::Renderer::Render(
  6452. IStream *pStm
  6453. )
  6454. {
  6455. HRESULT hResult = NO_ERROR;
  6456. WCHAR szText[MAX_PATH];
  6457. INT rgColIds[DetailsView::idCol_Last];
  6458. INT cCols = m_DV.GetColumnIds(rgColIds, ARRAYSIZE(rgColIds));
  6459. INT cRows = m_DV.GetReportRowCount();
  6460. INT i, j;
  6461. INT iRow = -1;
  6462. DBGASSERT((NULL != pStm));
  6463. m_Stm.SetStream(pStm);
  6464. //
  6465. // Start the report.
  6466. //
  6467. Begin(cRows, cCols);
  6468. //
  6469. // Add the report title.
  6470. //
  6471. m_DV.GetReportTitle(szText, ARRAYSIZE(szText));
  6472. AddTitle(szText);
  6473. //
  6474. // Add the report column headers.
  6475. //
  6476. BeginHeaders();
  6477. for (i = 0; i < cCols; i++)
  6478. {
  6479. m_DV.GetReportColHeader(rgColIds[i], szText, ARRAYSIZE(szText));
  6480. AddHeader(szText);
  6481. AddHeaderSep();
  6482. }
  6483. EndHeaders();
  6484. //
  6485. // Add the report row/col data.
  6486. //
  6487. for (i = 0; i < cRows; i++)
  6488. {
  6489. iRow = m_DV.GetNextSelectedItemIndex(iRow);
  6490. DBGASSERT((-1 != iRow));
  6491. BeginRow();
  6492. for (j = 0; j < cCols; j++)
  6493. {
  6494. AddRowColData(iRow, rgColIds[j]);
  6495. AddRowColSep();
  6496. }
  6497. EndRow();
  6498. }
  6499. //
  6500. // Terminate the report.
  6501. //
  6502. End();
  6503. }
  6504. ///////////////////////////////////////////////////////////////////////////////
  6505. /* Function: DetailsView::DataObject::Renderer::Stream::Stream
  6506. Description: Constructor for the renderer's private stream object.
  6507. The object is used to encapsulate stream write operations in overloaded
  6508. type-sensitive member functions.
  6509. Arguments:
  6510. pStm - Address of IStream associated with the object.
  6511. Returns: Nothing.
  6512. Revision History:
  6513. Date Description Programmer
  6514. -------- --------------------------------------------------- ----------
  6515. 05/20/97 Initial creation. BrianAu
  6516. */
  6517. ///////////////////////////////////////////////////////////////////////////////
  6518. DetailsView::DataObject::Renderer::Stream::Stream(
  6519. IStream *pStm
  6520. ) : m_pStm(pStm)
  6521. {
  6522. #ifdef CLIPBOARD_DEBUG_OUTPUT
  6523. m_pStgDbgOut = NULL;
  6524. m_pStmDbgOut = NULL;
  6525. StgCreateDocfile(TEXT("\\DskquotaClipboard.Out"),
  6526. STGM_CREATE |
  6527. STGM_READWRITE |
  6528. STGM_SHARE_EXCLUSIVE,
  6529. 0,
  6530. &m_pStgDbgOut);
  6531. if (NULL != m_pStgDbgOut)
  6532. {
  6533. m_pStgDbgOut->CreateStream(TEXT("Clipboard Data"),
  6534. STGM_CREATE |
  6535. STGM_READWRITE |
  6536. STGM_SHARE_EXCLUSIVE,
  6537. 0, 0,
  6538. &m_pStmDbgOut);
  6539. }
  6540. #endif // CLIPBOARD_DEBUG_OUTPUT
  6541. }
  6542. ///////////////////////////////////////////////////////////////////////////////
  6543. /* Function: DetailsView::DataObject::Renderer::Stream::~Stream
  6544. Description: Destructor for the renderer's private stream object.
  6545. Arguments: None.
  6546. Returns: Nothing.
  6547. Revision History:
  6548. Date Description Programmer
  6549. -------- --------------------------------------------------- ----------
  6550. 05/20/97 Initial creation. BrianAu
  6551. */
  6552. ///////////////////////////////////////////////////////////////////////////////
  6553. DetailsView::DataObject::Renderer::Stream::~Stream(VOID)
  6554. {
  6555. if (NULL != m_pStm)
  6556. m_pStm->Release();
  6557. #ifdef CLIPBOARD_DEBUG_OUTPUT
  6558. if (NULL != m_pStmDbgOut)
  6559. m_pStmDbgOut->Release();
  6560. if (NULL != m_pStgDbgOut)
  6561. m_pStgDbgOut->Release();
  6562. #endif // CLIPBOARD_DEBUG_OUTPUT
  6563. }
  6564. ///////////////////////////////////////////////////////////////////////////////
  6565. /* Function: DetailsView::DataObject::Renderer::Stream::SetStream
  6566. Description: Associates an IStream pointer with the stream object.
  6567. Releases an existing pointer if one was already assigned.
  6568. Arguments:
  6569. pStm - Address of new IStream to associate with stream object.
  6570. Caller must AddRef IStream pointer before passing to this function.
  6571. Returns: Nothing.
  6572. Revision History:
  6573. Date Description Programmer
  6574. -------- --------------------------------------------------- ----------
  6575. 05/20/97 Initial creation. BrianAu
  6576. */
  6577. ///////////////////////////////////////////////////////////////////////////////
  6578. VOID
  6579. DetailsView::DataObject::Renderer::Stream::SetStream(
  6580. IStream *pStm
  6581. )
  6582. {
  6583. DBGASSERT((NULL != pStm));
  6584. if (NULL != m_pStm)
  6585. m_pStm->Release();
  6586. m_pStm = pStm;
  6587. }
  6588. ///////////////////////////////////////////////////////////////////////////////
  6589. /* Function: DetailsView::DataObject::Renderer::Stream::Write
  6590. Description: Set of overloaded functions to handle
  6591. the writing of various types of data to the stream.
  6592. Arguments:
  6593. pbData - Address of BYTE buffer for source data.
  6594. cbData - Number of bytes in pbData[]
  6595. pszTextA - Ansi text string for source data.
  6596. pszTextW - Wide character text string for source data.
  6597. bData - Byte to write to stream.
  6598. chDataW - Wide character to write to stream.
  6599. chDataA - Ansi character to write to stream.
  6600. dwData - DWORD-type data to write to stream.
  6601. dblData - double-type data to write to stream.
  6602. Returns: Nothing.
  6603. Revision History:
  6604. Date Description Programmer
  6605. -------- --------------------------------------------------- ----------
  6606. 05/20/97 Initial creation. BrianAu
  6607. */
  6608. ///////////////////////////////////////////////////////////////////////////////
  6609. VOID
  6610. DetailsView::DataObject::Renderer::Stream::Write(
  6611. LPBYTE pbData,
  6612. UINT cbData
  6613. )
  6614. {
  6615. DBGASSERT((NULL != pbData));
  6616. ULONG cbWritten = 0;
  6617. HRESULT hr;
  6618. hr = m_pStm->Write(pbData, cbData, &cbWritten);
  6619. if (S_OK != hr)
  6620. {
  6621. DBGERROR((TEXT("Error 0x%08X writing to output stream."), hr));
  6622. CFileException::reason reason = CFileException::write;
  6623. switch(hr)
  6624. {
  6625. case STG_E_ACCESSDENIED:
  6626. reason = CFileException::access;
  6627. break;
  6628. case STG_E_MEDIUMFULL:
  6629. reason = CFileException::diskfull;
  6630. break;
  6631. case STG_E_WRITEFAULT:
  6632. reason = CFileException::device;
  6633. break;
  6634. default:
  6635. //
  6636. // Use default value.
  6637. //
  6638. break;
  6639. }
  6640. throw CFileException(reason, TEXT(""), 0);
  6641. }
  6642. #ifdef CLIPBOARD_DEBUG_OUTPUT
  6643. cbWritten = 0;
  6644. if (S_OK != m_pStmDbgOut->Write(pbData, cbData, &cbWritten))
  6645. throw CFileException(CFileException::write, TEXT(""), 0);
  6646. #endif // CLIPBOARD_DEBUG_OUTPUT
  6647. }
  6648. VOID
  6649. DetailsView::DataObject::Renderer::Stream::Write(
  6650. LPCWSTR pszTextW
  6651. )
  6652. {
  6653. Write((LPBYTE)pszTextW, lstrlenW(pszTextW) * sizeof(WCHAR));
  6654. }
  6655. VOID
  6656. DetailsView::DataObject::Renderer::Stream::Write(
  6657. LPCSTR pszTextA
  6658. )
  6659. {
  6660. Write((LPBYTE)pszTextA, lstrlenA(pszTextA) * sizeof(CHAR));
  6661. }
  6662. VOID
  6663. DetailsView::DataObject::Renderer::Stream::Write(
  6664. BYTE bData
  6665. )
  6666. {
  6667. Write((LPBYTE)&bData, sizeof(bData));
  6668. }
  6669. VOID
  6670. DetailsView::DataObject::Renderer::Stream::Write(
  6671. WCHAR chDataW
  6672. )
  6673. {
  6674. Write((LPBYTE)&chDataW, sizeof(chDataW));
  6675. }
  6676. VOID
  6677. DetailsView::DataObject::Renderer::Stream::Write(
  6678. CHAR chDataA
  6679. )
  6680. {
  6681. Write((LPBYTE)&chDataA, sizeof(chDataA));
  6682. }
  6683. VOID
  6684. DetailsView::DataObject::Renderer::Stream::Write(
  6685. DWORD dwData
  6686. )
  6687. {
  6688. Write((LPBYTE)&dwData, sizeof(dwData));
  6689. }
  6690. VOID
  6691. DetailsView::DataObject::Renderer::Stream::Write(
  6692. double dblData
  6693. )
  6694. {
  6695. Write((LPBYTE)&dblData, sizeof(dblData));
  6696. }
  6697. ///////////////////////////////////////////////////////////////////////////////
  6698. //
  6699. // The following section of code contains the different implementations of
  6700. // the virtual rendering functions that make each type of rendering object
  6701. // unique. Since they're pretty self-explanatory, I haven't commented each
  6702. // function. It should be obvious as to what they do.
  6703. // I have separated each rendering-type section with a banner comment for
  6704. // readability. [brianau]
  6705. //
  6706. ///////////////////////////////////////////////////////////////////////////////
  6707. ///////////////////////////////////////////////////////////////////////////////
  6708. // CF_UNICODETEXT
  6709. ///////////////////////////////////////////////////////////////////////////////
  6710. VOID
  6711. DetailsView::DataObject::Renderer_UNICODETEXT::AddTitle(
  6712. LPCTSTR pszTitle
  6713. )
  6714. {
  6715. m_Stm.Write(pszTitle);
  6716. m_Stm.Write(L'\r');
  6717. m_Stm.Write(L'\n');
  6718. m_Stm.Write(L'\r');
  6719. m_Stm.Write(L'\n');
  6720. }
  6721. VOID
  6722. DetailsView::DataObject::Renderer_UNICODETEXT::AddRowColData(
  6723. INT iRow,
  6724. INT idCol
  6725. )
  6726. {
  6727. WCHAR szText[MAX_PATH];
  6728. LV_REPORT_ITEM item;
  6729. item.fType = LVRI_TEXT; // Want text data.
  6730. item.pszText = szText;
  6731. item.cchMaxText = ARRAYSIZE(szText);
  6732. m_DV.GetReportItem(iRow, idCol, &item);
  6733. m_Stm.Write(szText);
  6734. }
  6735. ///////////////////////////////////////////////////////////////////////////////
  6736. // CF_TEXT
  6737. ///////////////////////////////////////////////////////////////////////////////
  6738. VOID
  6739. DetailsView::DataObject::Renderer_TEXT::AddTitle(
  6740. LPCWSTR pszTitleW
  6741. )
  6742. {
  6743. array_autoptr<CHAR> ptrTitleA(DataObject::WideToAnsi(pszTitleW));
  6744. m_Stm.Write(ptrTitleA);
  6745. m_Stm.Write('\r');
  6746. m_Stm.Write('\n');
  6747. m_Stm.Write('\r');
  6748. m_Stm.Write('\n');
  6749. }
  6750. VOID
  6751. DetailsView::DataObject::Renderer_TEXT::AddHeader(
  6752. LPCWSTR pszHeaderW
  6753. )
  6754. {
  6755. array_autoptr<CHAR> ptrHeaderA(DataObject::WideToAnsi(pszHeaderW));
  6756. m_Stm.Write(ptrHeaderA);
  6757. }
  6758. VOID
  6759. DetailsView::DataObject::Renderer_TEXT::AddRowColData(
  6760. INT iRow,
  6761. INT idCol
  6762. )
  6763. {
  6764. WCHAR szTextW[MAX_PATH];
  6765. LV_REPORT_ITEM item;
  6766. item.fType = LVRI_TEXT; // Want text data.
  6767. item.pszText = szTextW;
  6768. item.cchMaxText = ARRAYSIZE(szTextW);
  6769. m_DV.GetReportItem(iRow, idCol, &item);
  6770. array_autoptr<CHAR> ptrTextA(DataObject::WideToAnsi(szTextW));
  6771. m_Stm.Write(ptrTextA);
  6772. }
  6773. ///////////////////////////////////////////////////////////////////////////////
  6774. // RTF (Rich Text)
  6775. ///////////////////////////////////////////////////////////////////////////////
  6776. static const INT TWIPS_PER_PT = 20;
  6777. static const INT PTS_PER_INCH = 72;
  6778. static const INT TWIPS_PER_INCH = PTS_PER_INCH * TWIPS_PER_PT;
  6779. static const INT COL_WIDTH_TWIPS = TWIPS_PER_INCH * 5 / 4; // 1 1/4 inches.
  6780. //
  6781. // Converts all single backslashes to double backslashes.
  6782. // Literal backslashes in RTF must be "\\".
  6783. // Caller must delete[] the returned buffer.
  6784. //
  6785. LPSTR
  6786. DetailsView::DataObject::Renderer_RTF::DoubleBackslashes(
  6787. LPSTR pszSrc
  6788. )
  6789. {
  6790. DBGASSERT((NULL != pszSrc));
  6791. //
  6792. // Create new string for output. Size must be double. Every char
  6793. // could be '\'.
  6794. //
  6795. LPSTR pszFormatted = new CHAR[(lstrlenA(pszSrc) * 2) + 1];
  6796. LPSTR pszDest = pszFormatted;
  6797. while('\0' != *pszSrc)
  6798. {
  6799. if ('\\' == *pszSrc)
  6800. *pszDest++ = *pszSrc;
  6801. *pszDest++ = *pszSrc++;
  6802. }
  6803. *pszDest = *pszSrc; // Pick up NUL terminator.
  6804. return pszFormatted;
  6805. }
  6806. VOID
  6807. DetailsView::DataObject::Renderer_RTF::Begin(
  6808. INT cRows,
  6809. INT cCols
  6810. )
  6811. {
  6812. m_cCols = cCols;
  6813. m_Stm.Write("{\\rtf1 \\sect\\sectd\\lndscpsxn \\par\\pard\\plain ");
  6814. }
  6815. VOID
  6816. DetailsView::DataObject::Renderer_RTF::AddTitle(
  6817. LPCWSTR pszTitleW
  6818. )
  6819. {
  6820. array_autoptr<CHAR> ptrTempA(DataObject::WideToAnsi(pszTitleW));
  6821. array_autoptr<CHAR> ptrTitleA(DoubleBackslashes(ptrTempA)); // cvt '\' to "\\"
  6822. m_Stm.Write(ptrTitleA);
  6823. }
  6824. VOID DetailsView::DataObject::Renderer_RTF::BeginHeaders(
  6825. VOID
  6826. )
  6827. {
  6828. m_Stm.Write(" \\par \\par "); // Hdr preceded by empty row.
  6829. BeginHeaderOrRow(); // Add stuff common to hdr and data rows.
  6830. m_Stm.Write(" \\trhdr "); // Hdr at top of each page.
  6831. AddCellDefs(); // Cell size definitions.
  6832. }
  6833. VOID DetailsView::DataObject::Renderer_RTF::AddCellDefs(
  6834. VOID
  6835. )
  6836. {
  6837. char szText[80];
  6838. INT cxTwips = 0;
  6839. for (INT i = 0; i < m_cCols; i++)
  6840. {
  6841. cxTwips += COL_WIDTH_TWIPS;
  6842. wsprintfA(szText, "\\cellx%d", cxTwips);
  6843. m_Stm.Write(szText);
  6844. }
  6845. m_Stm.Write(' ');
  6846. }
  6847. //
  6848. // Stuff common to both header row and data rows.
  6849. //
  6850. VOID DetailsView::DataObject::Renderer_RTF::BeginHeaderOrRow(
  6851. VOID
  6852. )
  6853. {
  6854. m_Stm.Write("\\trowd \\pard \\intbl ");
  6855. }
  6856. VOID
  6857. DetailsView::DataObject::Renderer_RTF::AddHeader(
  6858. LPCWSTR pszHeaderW
  6859. )
  6860. {
  6861. array_autoptr<CHAR> ptrHeaderA(DataObject::WideToAnsi(pszHeaderW));
  6862. //
  6863. // No need to convert '\' to "\\". No
  6864. // backslashes in our header text.
  6865. //
  6866. m_Stm.Write(ptrHeaderA);
  6867. }
  6868. VOID
  6869. DetailsView::DataObject::Renderer_RTF::AddRowColData(
  6870. INT iRow,
  6871. INT idCol
  6872. )
  6873. {
  6874. WCHAR szTextW[MAX_PATH];
  6875. LV_REPORT_ITEM item;
  6876. item.fType = LVRI_TEXT; // Want text data.
  6877. item.pszText = szTextW;
  6878. item.cchMaxText = ARRAYSIZE(szTextW);
  6879. m_DV.GetReportItem(iRow, idCol, &item);
  6880. array_autoptr<CHAR> ptrTempA(DataObject::WideToAnsi(szTextW));
  6881. array_autoptr<CHAR> ptrTextA(DoubleBackslashes(ptrTempA)); // cvt '\' to "\\"
  6882. m_Stm.Write(ptrTextA);
  6883. }
  6884. ///////////////////////////////////////////////////////////////////////////////
  6885. // Private import/export format
  6886. ///////////////////////////////////////////////////////////////////////////////
  6887. //
  6888. // Assumes that caller AddRef'd IStream pointer.
  6889. //
  6890. VOID
  6891. DetailsView::DataObject::Renderer_Export::Render(
  6892. IStream *pStm
  6893. )
  6894. {
  6895. HRESULT hResult = NO_ERROR;
  6896. INT cRows = m_DV.GetReportRowCount();
  6897. INT iRow = -1;
  6898. DBGASSERT((NULL != pStm));
  6899. m_Stm.SetStream(pStm);
  6900. Begin(cRows, 0);
  6901. //
  6902. // Add the export data records.
  6903. //
  6904. for (INT i = 0; i < cRows; i++)
  6905. {
  6906. iRow = m_DV.GetNextSelectedItemIndex(iRow);
  6907. DBGASSERT((-1 != iRow));
  6908. AddBinaryRecord(iRow);
  6909. }
  6910. //
  6911. // Terminate the report.
  6912. //
  6913. End();
  6914. }
  6915. VOID
  6916. DetailsView::DataObject::Renderer_Export::Begin(
  6917. INT cRows,
  6918. INT cCols
  6919. )
  6920. {
  6921. //
  6922. // The stream header contains a GUID as a unique identifier followed
  6923. // by a version number.
  6924. //
  6925. m_Stm.Write((LPBYTE)&GUID_NtDiskQuotaStream, sizeof(GUID_NtDiskQuotaStream));
  6926. m_Stm.Write(DataObject::EXPORT_STREAM_VERSION);
  6927. m_Stm.Write((DWORD)cRows);
  6928. }
  6929. VOID
  6930. DetailsView::DataObject::Renderer_Export::AddBinaryRecord(
  6931. INT iRow
  6932. )
  6933. {
  6934. INT cbRecord = m_DV.GetReportBinaryRecordSize(iRow);
  6935. array_autoptr<BYTE> ptrRecord(new BYTE[cbRecord]);
  6936. if (NULL != ptrRecord.get())
  6937. {
  6938. if (m_DV.GetReportBinaryRecord(iRow, ptrRecord, cbRecord))
  6939. {
  6940. m_Stm.Write(ptrRecord, cbRecord);
  6941. }
  6942. }
  6943. }
  6944. ///////////////////////////////////////////////////////////////////////////////
  6945. // CF "FileGroupDescriptor"
  6946. //
  6947. //
  6948. ///////////////////////////////////////////////////////////////////////////////
  6949. VOID
  6950. DetailsView::DataObject::Renderer_FileGroupDescriptor::Begin(
  6951. INT cRows,
  6952. INT cCols
  6953. )
  6954. {
  6955. //
  6956. // Build a name for the file we'll create.
  6957. //
  6958. // Vol label? Filename
  6959. // ---------- -------------------------------------------------------
  6960. // Yes "Disk Quota Settings for Volume 'VOL_LABEL'"
  6961. // No "Disk Quota Settings for Unlabeled Volume SN 8AB1-DE23"
  6962. //
  6963. // The serial-number format is gross but without a label, we don't have
  6964. // any other distinguishing feature for the volume. I'd use the
  6965. // display name from the CVolumeID object but in the mounted volume
  6966. // case, it contains backslashes and a colon; both invalid buried in
  6967. // a filename.
  6968. //
  6969. TCHAR szLabel[MAX_VOL_LABEL] = { TEXT('\0') };
  6970. DWORD dwSerialNumber = 0;
  6971. GetVolumeInformation(m_DV.GetVolumeID().ForParsing(),
  6972. szLabel,
  6973. ARRAYSIZE(szLabel),
  6974. &dwSerialNumber,
  6975. NULL,
  6976. NULL,
  6977. NULL,
  6978. 0);
  6979. CString strFileName;
  6980. if (TEXT('\0') != szLabel[0])
  6981. {
  6982. //
  6983. // Volume has a label.
  6984. //
  6985. strFileName.Format(g_hInstDll,
  6986. IDS_EXPORT_STREAM_FILENAME_TEMPLATE,
  6987. szLabel);
  6988. }
  6989. else
  6990. {
  6991. //
  6992. // No volume label.
  6993. //
  6994. strFileName.Format(g_hInstDll,
  6995. IDS_EXPORT_STREAM_FILENAME_TEMPLATE_VOLSN,
  6996. HIWORD(dwSerialNumber),
  6997. LOWORD(dwSerialNumber));
  6998. }
  6999. //
  7000. // Create a file group descriptor containing the name we want the
  7001. // shell to use for the file. The descriptor contains one file
  7002. // description. That description just contains the file name.
  7003. // All other members are initialized to 0.
  7004. //
  7005. FILEGROUPDESCRIPTORW desc;
  7006. ZeroMemory(&desc, sizeof(desc));
  7007. desc.cItems = 1;
  7008. lstrcpyn(desc.fgd[0].cFileName, strFileName, ARRAYSIZE(desc.fgd[0].cFileName));
  7009. //
  7010. // Write the file group descriptor to the renderer's stream.
  7011. //
  7012. m_Stm.Write((LPBYTE)&desc, sizeof(desc));
  7013. }
  7014. ///////////////////////////////////////////////////////////////////////////////
  7015. /* Function: LVSelection::Add
  7016. Description: Add a user pointer and listview item index to a listview
  7017. selection object. This object is used to transfer the notion of a
  7018. "selection" to some function.
  7019. Arguments:
  7020. pUser - Address of IDiskQuotaUser interface for a selected user object.
  7021. iItem - Index of selected item in the listview.
  7022. Returns: Nothing.
  7023. Revision History:
  7024. Date Description Programmer
  7025. -------- --------------------------------------------------- ----------
  7026. 09/10/96 Initial creation. BrianAu
  7027. */
  7028. ///////////////////////////////////////////////////////////////////////////////
  7029. VOID
  7030. LVSelection::Add(
  7031. PDISKQUOTA_USER pUser,
  7032. INT iItem
  7033. )
  7034. {
  7035. DBGASSERT((NULL != pUser));
  7036. ListEntry entry;
  7037. entry.pUser = pUser;
  7038. entry.iItem = iItem;
  7039. m_List.Append((LPVOID)&entry);
  7040. }
  7041. ///////////////////////////////////////////////////////////////////////////////
  7042. /* Function: LVSelection::Retrieve
  7043. Description: Retrieves a user pointer and listview item index from a
  7044. listview selection object.
  7045. Arguments:
  7046. i - Index of item. Use the Count() method to determine how many
  7047. items are in the selection object.
  7048. ppUser - Address of an interface pointer variable to receive the
  7049. IDiskQuotaUser interface for the user object at index 'i'.
  7050. piItem - Address of integer variable to receive the Listview item index
  7051. of the object at index 'i'.
  7052. Returns: TRUE = Returned information is valid.
  7053. FALSE = Couldn't retrieve entry 'i'.
  7054. Revision History:
  7055. Date Description Programmer
  7056. -------- --------------------------------------------------- ----------
  7057. 09/10/96 Initial creation. BrianAu
  7058. */
  7059. ///////////////////////////////////////////////////////////////////////////////
  7060. BOOL
  7061. LVSelection::Retrieve(
  7062. INT i,
  7063. PDISKQUOTA_USER *ppUser,
  7064. INT *piItem
  7065. )
  7066. {
  7067. ListEntry entry;
  7068. if (m_List.Retrieve((LPVOID)&entry, i))
  7069. {
  7070. if (NULL != ppUser)
  7071. *ppUser = entry.pUser;
  7072. if (NULL != piItem)
  7073. *piItem = entry.iItem;
  7074. return TRUE;
  7075. }
  7076. return FALSE;
  7077. }
  7078. ///////////////////////////////////////////////////////////////////////////////
  7079. /* Function: ColumnMap::ColumnMap
  7080. Function: ColumnMap::~ColumnMap
  7081. Description: Constructor and Destructor.
  7082. Creates/Destroys a column map. The column map is used
  7083. to map column ID's (known to the Details View) to listview subitem
  7084. indices. It is needed to support the addition and deletion of the
  7085. folder name column.
  7086. Arguments:
  7087. cMapSize - Number of entries in the map. Should be the max number
  7088. of columns possible in the listview.
  7089. Returns: Nothing.
  7090. Exceptions: OutOfMemory.
  7091. Revision History:
  7092. Date Description Programmer
  7093. -------- --------------------------------------------------- ----------
  7094. 10/09/96 Initial creation. BrianAu
  7095. */
  7096. ///////////////////////////////////////////////////////////////////////////////
  7097. ColumnMap::ColumnMap(
  7098. UINT cMapSize
  7099. ) : m_pMap(NULL),
  7100. m_cMapSize(cMapSize)
  7101. {
  7102. //
  7103. // Can throw OutOfMemory.
  7104. //
  7105. m_pMap = new INT[m_cMapSize];
  7106. FillMemory(m_pMap, m_cMapSize * sizeof(m_pMap[0]), (BYTE)-1);
  7107. }
  7108. ColumnMap::~ColumnMap(
  7109. VOID
  7110. )
  7111. {
  7112. if (NULL != m_pMap)
  7113. delete[] m_pMap;
  7114. }
  7115. ///////////////////////////////////////////////////////////////////////////////
  7116. /* Function: ColumnMap::SubItemToId
  7117. Description: Returns a column ID given a listview subitem index.
  7118. Arguments:
  7119. iSubItem - 0-based subitem index of the item to be mapped.
  7120. Returns: Column ID corresponding to subitem. -1 if the subitem is invalid.
  7121. Revision History:
  7122. Date Description Programmer
  7123. -------- --------------------------------------------------- ----------
  7124. 10/09/96 Initial creation. BrianAu
  7125. */
  7126. ///////////////////////////////////////////////////////////////////////////////
  7127. INT
  7128. ColumnMap::SubItemToId(
  7129. INT iSubItem
  7130. ) const
  7131. {
  7132. DBGASSERT((iSubItem >= 0 && iSubItem < (INT)m_cMapSize));
  7133. return *(m_pMap + iSubItem);
  7134. }
  7135. ///////////////////////////////////////////////////////////////////////////////
  7136. /* Function: ColumnMap::IdToSubItem
  7137. Description: Returns a listview subitem index given a column ID.
  7138. Arguments:
  7139. iColId - ID of column. i.e. idCol_Name, idCol_Folder etc.
  7140. Returns: Listview subitem index. -1 if the column is not currently
  7141. visible.
  7142. Revision History:
  7143. Date Description Programmer
  7144. -------- --------------------------------------------------- ----------
  7145. 10/09/96 Initial creation. BrianAu
  7146. */
  7147. ///////////////////////////////////////////////////////////////////////////////
  7148. INT
  7149. ColumnMap::IdToSubItem(
  7150. INT iColId
  7151. ) const
  7152. {
  7153. for (INT i = 0; i < (INT)m_cMapSize; i++)
  7154. {
  7155. if (SubItemToId(i) == iColId)
  7156. return i;
  7157. }
  7158. return -1;
  7159. }
  7160. ///////////////////////////////////////////////////////////////////////////////
  7161. /* Function: ColumnMap::RemoveId
  7162. Description: Removes a mapping for a given listview subitem index.
  7163. Arguments:
  7164. iSubItem - 0-based subitem index of the item to be removed.
  7165. Returns: Nothing.
  7166. Revision History:
  7167. Date Description Programmer
  7168. -------- --------------------------------------------------- ----------
  7169. 10/09/96 Initial creation. BrianAu
  7170. 11/30/96 Fixed off-by-one error. BrianAu
  7171. */
  7172. ///////////////////////////////////////////////////////////////////////////////
  7173. VOID
  7174. ColumnMap::RemoveId(
  7175. INT iSubItem
  7176. )
  7177. {
  7178. DBGASSERT((iSubItem >= 0 && iSubItem < (INT)m_cMapSize));
  7179. for (INT i = iSubItem; i < (INT)m_cMapSize - 1; i++)
  7180. *(m_pMap + i) = *(m_pMap + i + 1);
  7181. *(m_pMap + m_cMapSize - 1) = -1;
  7182. }
  7183. ///////////////////////////////////////////////////////////////////////////////
  7184. /* Function: ColumnMap::InsertId
  7185. Description: Adds a mapping for a given listview subitem index.
  7186. The mapping is added at the iSubItem location in the map. All subsequent
  7187. item mappings are shifted down one place. This is analogous to
  7188. inserting a column into the listview.
  7189. Arguments:
  7190. iSubItem - 0-based subitem index of the item to be removed.
  7191. iColId - ID of column. i.e. idCol_Name, idCol_Folder etc.
  7192. Returns: Nothing.
  7193. Revision History:
  7194. Date Description Programmer
  7195. -------- --------------------------------------------------- ----------
  7196. 10/09/96 Initial creation. BrianAu
  7197. 11/30/96 Fixed off-by-one error. BrianAu
  7198. */
  7199. ///////////////////////////////////////////////////////////////////////////////
  7200. VOID
  7201. ColumnMap::InsertId(
  7202. INT iSubItem,
  7203. INT iColId
  7204. )
  7205. {
  7206. DBGASSERT((iSubItem >= 0 && iSubItem < (INT)m_cMapSize));
  7207. for (INT i = m_cMapSize-1; i > iSubItem; i--)
  7208. *(m_pMap + i) = *(m_pMap + i - 1);
  7209. *(m_pMap + iSubItem) = iColId;
  7210. }
  7211. ///////////////////////////////////////////////////////////////////////////////
  7212. /* Function: DetailsView::Finder::Finder
  7213. Description: Constructs a user finder object.
  7214. The user finder coordinates the activities of finding an item in
  7215. the details listview.
  7216. Arguments:
  7217. DetailsView - Reference to the details view object.
  7218. cMaxMru - Maximum entries allowed in the most-recently-used list.
  7219. This list is maintained in the dropdown combo box in the
  7220. view's toolbar.
  7221. Returns: Nothing.
  7222. Revision History:
  7223. Date Description Programmer
  7224. -------- --------------------------------------------------- ----------
  7225. 05/20/97 Initial creation. BrianAu
  7226. */
  7227. ///////////////////////////////////////////////////////////////////////////////
  7228. DetailsView::Finder::Finder(
  7229. DetailsView& DetailsView,
  7230. INT cMaxMru
  7231. ) : m_DetailsView(DetailsView),
  7232. m_hwndToolbarCombo(NULL),
  7233. m_cMaxComboEntries(cMaxMru),
  7234. m_pfnOldToolbarComboWndProc(NULL)
  7235. {
  7236. //
  7237. // Nothing more to do.
  7238. //
  7239. }
  7240. ///////////////////////////////////////////////////////////////////////////////
  7241. /* Function: DetailsView::Finder::ConnectToolbarCombo
  7242. Description: Connects the finder object to the combo box in the view's
  7243. toolbar. This is necessary because the finder object coordinates
  7244. the contents of the toolbar combo box with the contents of the
  7245. combo box in the "Find User" dialog. When you enter a name in
  7246. one of the boxes, it is automatically added to the other so they
  7247. appear to be in sync.
  7248. Also subclasses the edit control within the combo box. This is
  7249. required so that we can intercept VK_RETURN and find the record
  7250. when the user presses [Return].
  7251. Also adds the toolbar combo box as a "tool" to the toolbar. This
  7252. is so we can get a tooltip for the combo.
  7253. Arguments:
  7254. hwndToolbarCombo - Hwnd of combo box in view's toolbar.
  7255. Returns: Nothing.
  7256. Revision History:
  7257. Date Description Programmer
  7258. -------- --------------------------------------------------- ----------
  7259. 05/20/97 Initial creation. BrianAu
  7260. */
  7261. ///////////////////////////////////////////////////////////////////////////////
  7262. VOID
  7263. DetailsView::Finder::ConnectToolbarCombo(
  7264. HWND hwndToolbarCombo
  7265. )
  7266. {
  7267. m_hwndToolbarCombo = hwndToolbarCombo;
  7268. //
  7269. // Add the combo box to the toolbar's list of "tools".
  7270. // This will allow us to get a tooltip for the combo box.
  7271. // This code assumes that the combo is a child of the toolbar.
  7272. //
  7273. HWND hwndToolbar = GetParent(hwndToolbarCombo);
  7274. HWND hwndMain = GetParent(hwndToolbar);
  7275. HWND hwndTooltip = (HWND)SendMessage(hwndToolbar,
  7276. TB_GETTOOLTIPS,
  7277. 0, 0);
  7278. if (NULL != hwndTooltip)
  7279. {
  7280. TOOLINFO ti;
  7281. ti.cbSize = sizeof(ti);
  7282. ti.uFlags = TTF_IDISHWND | TTF_CENTERTIP | TTF_SUBCLASS;
  7283. ti.lpszText = (LPTSTR)IDS_TOOLBAR_COMBO;
  7284. ti.hwnd = hwndMain;
  7285. ti.uId = (UINT_PTR)hwndToolbarCombo;
  7286. ti.hinst = g_hInstDll;
  7287. SendMessage(hwndTooltip, TTM_ADDTOOL, 0, (LPARAM)&ti);
  7288. }
  7289. //
  7290. // Subclass the combo box so we can intercept VK_ENTER.
  7291. // This is done so we can respond to VK_ENTER. Normally combo boxes
  7292. // don't respond to this keystroke.
  7293. //
  7294. HWND hwndComboEdit = NULL;
  7295. //
  7296. // The combo box has two children... an edit control and a listbox control.
  7297. // Find the edit control.
  7298. //
  7299. for (HWND hwndChild = GetTopWindow(m_hwndToolbarCombo);
  7300. hwndChild != NULL;
  7301. hwndChild = GetNextWindow(hwndChild, GW_HWNDNEXT))
  7302. {
  7303. TCHAR szClassName[20] = { TEXT('\0') };
  7304. GetClassName(hwndChild, szClassName, ARRAYSIZE(szClassName));
  7305. if (0 == lstrcmpi(szClassName, TEXT("Edit")))
  7306. {
  7307. hwndComboEdit = hwndChild;
  7308. break;
  7309. }
  7310. }
  7311. if (NULL != hwndComboEdit)
  7312. {
  7313. //
  7314. // Store the address of the Finder object in the combo box's
  7315. // userdata. This is so the subclass WndProc (a static function)
  7316. // can access the finder object.
  7317. //
  7318. SetWindowLongPtr(hwndComboEdit, GWLP_USERDATA, (INT_PTR)this);
  7319. //
  7320. // Subclass the combo box's edit control.
  7321. //
  7322. m_pfnOldToolbarComboWndProc = (WNDPROC)GetWindowLongPtr(hwndComboEdit,
  7323. GWLP_WNDPROC);
  7324. SetWindowLongPtr(hwndComboEdit,
  7325. GWLP_WNDPROC,
  7326. (INT_PTR)DetailsView::Finder::ToolbarComboSubClassWndProc);
  7327. }
  7328. }
  7329. ///////////////////////////////////////////////////////////////////////////////
  7330. /* Function: DetailsView::Finder::InvokeFindDialog
  7331. Description: Display the "Find User" dialog.
  7332. Arguments:
  7333. hwndParent - Parent for the dialog.
  7334. Returns: Nothing.
  7335. Revision History:
  7336. Date Description Programmer
  7337. -------- --------------------------------------------------- ----------
  7338. 05/20/97 Initial creation. BrianAu
  7339. */
  7340. ///////////////////////////////////////////////////////////////////////////////
  7341. VOID
  7342. DetailsView::Finder::InvokeFindDialog(
  7343. HWND hwndParent
  7344. )
  7345. {
  7346. DialogBoxParam(g_hInstDll,
  7347. MAKEINTRESOURCE(IDD_FINDUSER),
  7348. hwndParent,
  7349. DetailsView::Finder::DlgProc,
  7350. (LPARAM)this);
  7351. }
  7352. ///////////////////////////////////////////////////////////////////////////////
  7353. /* Function: DetailsView::Finder::DlgProc
  7354. Description: DlgProc for the "Find User" dialog.
  7355. Arguments:
  7356. Standard DlgProc arguments.
  7357. Returns:
  7358. Standard DlgProc return values.
  7359. Revision History:
  7360. Date Description Programmer
  7361. -------- --------------------------------------------------- ----------
  7362. 05/20/97 Initial creation. BrianAu
  7363. */
  7364. ///////////////////////////////////////////////////////////////////////////////
  7365. INT_PTR CALLBACK
  7366. DetailsView::Finder::DlgProc(
  7367. HWND hwnd,
  7368. UINT uMsg,
  7369. WPARAM wParam,
  7370. LPARAM lParam
  7371. )
  7372. {
  7373. //
  7374. // Get the finder object's "this" ptr from the window's userdata.
  7375. //
  7376. Finder *pThis = (Finder *)GetWindowLongPtr(hwnd, DWLP_USER);
  7377. switch(uMsg)
  7378. {
  7379. case WM_INITDIALOG:
  7380. {
  7381. //
  7382. // Save the "this" ptr in the window's userdata.
  7383. //
  7384. pThis = (Finder *)lParam;
  7385. SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)pThis);
  7386. //
  7387. // Set the height of the combo in the dialog.
  7388. // Not sure why, but DevStudio's dialog editor won't let me
  7389. // do this. Use the same height value we use for the combo
  7390. // in the toolbar. It's the same contents so the height
  7391. // should be the same.
  7392. //
  7393. HWND hwndCombo = GetDlgItem(hwnd, IDC_CMB_FINDUSER);
  7394. RECT rcCombo;
  7395. GetClientRect(hwndCombo, &rcCombo);
  7396. SetWindowPos(hwndCombo,
  7397. NULL,
  7398. 0, 0,
  7399. rcCombo.right - rcCombo.left,
  7400. CY_TOOLBAR_COMBO,
  7401. SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
  7402. //
  7403. // Fill the dialog's combo with entries from the toolbar
  7404. // combo. The toolbar's combo box contains the MRU for finding users.
  7405. //
  7406. pThis->FillDialogCombo(pThis->m_hwndToolbarCombo, GetDlgItem(hwnd, IDC_CMB_FINDUSER));
  7407. return 1;
  7408. }
  7409. case WM_COMMAND:
  7410. switch(LOWORD(wParam))
  7411. {
  7412. case IDOK:
  7413. //
  7414. // User pressed OK button or [Enter].
  7415. //
  7416. DBGASSERT((NULL != pThis));
  7417. if (!pThis->UserNameEntered(GetDlgItem(hwnd, IDC_CMB_FINDUSER)))
  7418. {
  7419. //
  7420. // Record not found so don't close dialog.
  7421. // UserNameEntered() will display UI to tell the user
  7422. // that the name wasn't found. Leave the dialog open
  7423. // so user can retry with a new name.
  7424. //
  7425. break;
  7426. }
  7427. //
  7428. // Fall through...
  7429. //
  7430. case IDCANCEL:
  7431. //
  7432. // User pressed Cancel button or [ESC].
  7433. //
  7434. EndDialog(hwnd, 0);
  7435. break;
  7436. default:
  7437. break;
  7438. }
  7439. break;
  7440. };
  7441. return 0;
  7442. }
  7443. ///////////////////////////////////////////////////////////////////////////////
  7444. /* Function: DetailsView::Finder::FillDialogCombo
  7445. Description: Fill the combo box in the dialog with the contents
  7446. from a second combo box.
  7447. Arguments:
  7448. hwndComboSrc - Hwnd of source combo containing text strings.
  7449. hwndComboDest - Hwnd of combo where strings will be copied to.
  7450. Returns: Nothing.
  7451. Revision History:
  7452. Date Description Programmer
  7453. -------- --------------------------------------------------- ----------
  7454. 05/20/97 Initial creation. BrianAu
  7455. */
  7456. ///////////////////////////////////////////////////////////////////////////////
  7457. VOID
  7458. DetailsView::Finder::FillDialogCombo(
  7459. HWND hwndComboSrc,
  7460. HWND hwndComboDest
  7461. )
  7462. {
  7463. //
  7464. // Clear out the destination combo.
  7465. //
  7466. SendMessage(hwndComboDest, CB_RESETCONTENT, 0, 0);
  7467. //
  7468. // Copy all contents of the source combo to the destination combo.
  7469. //
  7470. INT cItems = (INT)SendMessage(hwndComboSrc, CB_GETCOUNT, 0, 0);
  7471. if (CB_ERR != cItems)
  7472. {
  7473. for (INT i = 0; i < cItems; i++)
  7474. {
  7475. LPTSTR pszName = NULL;
  7476. INT cchName = (INT)SendMessage(hwndComboSrc, CB_GETLBTEXTLEN, i, 0);
  7477. pszName = new TCHAR[cchName + 1];
  7478. if (NULL != pszName)
  7479. {
  7480. //
  7481. // Remove item from the source combo at index [i] and append
  7482. // it to the destination combo.
  7483. //
  7484. SendMessage(hwndComboSrc, CB_GETLBTEXT, i, (LPARAM)pszName);
  7485. SendMessage(hwndComboDest, CB_ADDSTRING, 0, (LPARAM)pszName);
  7486. delete[] pszName;
  7487. }
  7488. }
  7489. }
  7490. }
  7491. ///////////////////////////////////////////////////////////////////////////////
  7492. /* Function: DetailsView::Finder::ToolbarComboSubclassWndProc
  7493. Description: Subclass window procedure for the "Edit" control that is part
  7494. of the combo contained in the view's toolbar. We subclass this control
  7495. so that we can intercept VK_RETURN and handle it. The standard combo
  7496. box just beeps when you press [Enter] in it's edit control.
  7497. Arguments:
  7498. Standard WndProc arguments.
  7499. Returns:
  7500. Standard WndProc return values.
  7501. Revision History:
  7502. Date Description Programmer
  7503. -------- --------------------------------------------------- ----------
  7504. 05/20/97 Initial creation. BrianAu
  7505. */
  7506. ///////////////////////////////////////////////////////////////////////////////
  7507. LRESULT CALLBACK
  7508. DetailsView::Finder::ToolbarComboSubClassWndProc(
  7509. HWND hwnd,
  7510. UINT uMsg,
  7511. WPARAM wParam,
  7512. LPARAM lParam
  7513. )
  7514. {
  7515. //
  7516. // Get finder object's "this" ptr from window's userdata.
  7517. //
  7518. Finder *pThis = (Finder *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  7519. switch(uMsg)
  7520. {
  7521. case WM_CHAR:
  7522. switch(wParam)
  7523. {
  7524. case VK_RETURN:
  7525. {
  7526. //
  7527. // Tell the finder that a user name was entered in the
  7528. // combo box. Pass the hwnd of the combo from which the
  7529. // name was entered. Since this message is for the
  7530. // subclassed edit control (child of the combo), the
  7531. // parent is the combo box itself.
  7532. //
  7533. DBGASSERT((NULL != pThis));
  7534. HWND hwndCombo = GetParent(hwnd);
  7535. if (pThis->UserNameEntered(hwndCombo))
  7536. {
  7537. //
  7538. // Record found in view.
  7539. // Set focus to the main view.
  7540. // If not found, focus should just stay with the combo
  7541. // so user can enter another name.
  7542. //
  7543. HWND hwndToolbar = GetParent(hwndCombo);
  7544. SetFocus(GetParent(hwndToolbar));
  7545. }
  7546. else
  7547. {
  7548. //
  7549. // Not found in listview. Focus remains in the combo box
  7550. // so user can try again with a new name.
  7551. //
  7552. SetFocus(hwndCombo);
  7553. }
  7554. //
  7555. // Swallow up the VK_RETURN.
  7556. // Otherwise, the combo box control beeps.
  7557. //
  7558. return 0;
  7559. }
  7560. case VK_ESCAPE:
  7561. {
  7562. //
  7563. // Set focus to the main window which will set focus to the
  7564. // listview. This gives the keyboard-only user a way to
  7565. // get back out of the combo box.
  7566. //
  7567. HWND hwndCombo = GetParent(hwnd);
  7568. HWND hwndToolbar = GetParent(hwndCombo);
  7569. SetFocus(GetParent(hwndToolbar));
  7570. //
  7571. // Swallow VK_ESCAPE so combo box doesn't beep.
  7572. //
  7573. return 0;
  7574. }
  7575. }
  7576. break;
  7577. default:
  7578. break;
  7579. }
  7580. return CallWindowProc(pThis->m_pfnOldToolbarComboWndProc,
  7581. hwnd, uMsg, wParam, lParam);
  7582. }
  7583. ///////////////////////////////////////////////////////////////////////////////
  7584. /* Function: DetailsView::Finder::AddNameToCombo
  7585. Description: Add a name string to one of the Find User combo boxes.
  7586. If the item already exists in the list, it is moved to the top of the
  7587. list. If the item is not in the list, it is added at the top of the list.
  7588. If the addition of the new item causes the list's entry count to exceed
  7589. a specified maximum value, the last item in the list is removed.
  7590. Arguments:
  7591. hwndCombo - Hwnd for the combo box to which the name is added.
  7592. pszName - Address of name string to add.
  7593. cMaxEntries - Maximum number of entries allowed in combo box.
  7594. Returns: Nothing.
  7595. Revision History:
  7596. Date Description Programmer
  7597. -------- --------------------------------------------------- ----------
  7598. 05/20/97 Initial creation. BrianAu
  7599. */
  7600. ///////////////////////////////////////////////////////////////////////////////
  7601. VOID
  7602. DetailsView::Finder::AddNameToCombo(
  7603. HWND hwndCombo,
  7604. LPCTSTR pszName,
  7605. INT cMaxEntries
  7606. )
  7607. {
  7608. if (NULL != pszName && TEXT('\0') != *pszName)
  7609. {
  7610. //
  7611. // See if the item already exists in the list.
  7612. //
  7613. INT iItemToDelete = (INT)SendMessage(hwndCombo,
  7614. CB_FINDSTRING,
  7615. (WPARAM)-1,
  7616. (LPARAM)pszName);
  7617. if (CB_ERR == iItemToDelete)
  7618. {
  7619. //
  7620. // Item is not already in the list. Need to add it.
  7621. // If the list is full, we'll have to drop one off of the end.
  7622. //
  7623. INT cItems = (INT)SendMessage(hwndCombo, CB_GETCOUNT, 0, 0);
  7624. if (CB_ERR != cItems && 0 < cItems && cItems >= cMaxEntries)
  7625. {
  7626. iItemToDelete = cItems - 1;
  7627. }
  7628. }
  7629. if (-1 != iItemToDelete)
  7630. {
  7631. //
  7632. // Need to delete an existing item for one of these reasons:
  7633. //
  7634. // 1. Promoting an existing item to the head of the list.
  7635. // Delete it from it's previous location.
  7636. // 2. Dropping last item from list.
  7637. //
  7638. SendMessage(hwndCombo, CB_DELETESTRING, iItemToDelete, 0);
  7639. }
  7640. //
  7641. // Add the new item at the head of the list.
  7642. //
  7643. SendMessage(hwndCombo, CB_INSERTSTRING, 0, (LPARAM)pszName);
  7644. }
  7645. }
  7646. ///////////////////////////////////////////////////////////////////////////////
  7647. /* Function: DetailsView::Finder::UserNameEntered
  7648. Description: A name has been entered from one of the combo boxes.
  7649. 1. Retrieve the name from the combo.
  7650. 2. See if it's in the listview and if it is, the listview ensures the
  7651. item is visible and selects it.
  7652. 3. Update the toobar combo's list with the new item. This is our MRU
  7653. list.
  7654. Add a name string to one of the Find User combo boxes.
  7655. If the item already exists in the list, it is moved to the top of the
  7656. list. If the item is not in the list, it is added at the top of the list.
  7657. If the addition of the new item causes the list's entry count to exceed
  7658. a specified maximum value, the last item in the list is removed.
  7659. Arguments:
  7660. hwndCombo - Hwnd for the combo box to which the name is added.
  7661. pszName - Address of name string to add.
  7662. cMaxEntries - Maximum number of entries allowed in combo box.
  7663. Returns:
  7664. TRUE = User was found in listview.
  7665. FALSE = User was not found in listview.
  7666. Revision History:
  7667. Date Description Programmer
  7668. -------- --------------------------------------------------- ----------
  7669. 05/20/97 Initial creation. BrianAu
  7670. */
  7671. ///////////////////////////////////////////////////////////////////////////////
  7672. BOOL
  7673. DetailsView::Finder::UserNameEntered(
  7674. HWND hwndCombo
  7675. )
  7676. {
  7677. TCHAR szName[MAX_PATH] = { TEXT('\0') };
  7678. BOOL bUserFoundInListView = FALSE;
  7679. //
  7680. // Get the name from the combo edit control.
  7681. //
  7682. if (0 < SendMessage(hwndCombo, WM_GETTEXT, (WPARAM)ARRAYSIZE(szName), (LPARAM)szName))
  7683. {
  7684. //
  7685. // Tell the details view object to highlight this name.
  7686. //
  7687. bUserFoundInListView = m_DetailsView.GotoUserName(szName);
  7688. if (bUserFoundInListView)
  7689. {
  7690. //
  7691. // Add the name to the toolbar combo's listbox. This becomes
  7692. // our MRU list. Also make sure the visible name in the combo's
  7693. // edit control is the one last entered. May have been entered
  7694. // through the "Find User" dialog.
  7695. //
  7696. AddNameToCombo(m_hwndToolbarCombo, szName, m_cMaxComboEntries);
  7697. SendMessage(m_hwndToolbarCombo, WM_SETTEXT, 0, (LPARAM)szName);
  7698. }
  7699. else
  7700. {
  7701. //
  7702. // Display a message box to the user stating that the user couldn't
  7703. // be found in the listview.
  7704. //
  7705. CString strMsg(g_hInstDll, IDS_USER_NOT_FOUND_IN_LISTVIEW, szName);
  7706. DiskQuotaMsgBox(hwndCombo,
  7707. (LPCTSTR)strMsg,
  7708. IDS_TITLE_DISK_QUOTA,
  7709. MB_ICONEXCLAMATION);
  7710. }
  7711. }
  7712. return bUserFoundInListView;
  7713. }
  7714. DetailsView::Importer::Importer(
  7715. DetailsView& DV
  7716. ) : m_DV(DV),
  7717. m_bUserCancelled(FALSE),
  7718. m_bPromptOnReplace(TRUE),
  7719. m_dlgProgress(IDD_PROGRESS,
  7720. IDC_PROGRESS_BAR,
  7721. IDC_TXT_PROGRESS_DESCRIPTION,
  7722. IDC_TXT_PROGRESS_FILENAME),
  7723. m_hwndParent(m_DV.m_hwndMain),
  7724. m_cImported(0)
  7725. {
  7726. if (m_dlgProgress.Create(g_hInstDll, m_hwndParent))
  7727. {
  7728. EnableWindow(m_hwndParent, FALSE);
  7729. m_dlgProgress.SetDescription(MAKEINTRESOURCE(IDS_PROGRESS_IMPORTING));
  7730. m_dlgProgress.Show();
  7731. }
  7732. //
  7733. // Clear any previous undo actions from the view's undo list.
  7734. //
  7735. m_DV.m_pUndoList->Clear();
  7736. }
  7737. DetailsView::Importer::~Importer(
  7738. VOID
  7739. )
  7740. {
  7741. Destroy();
  7742. }
  7743. VOID
  7744. DetailsView::Importer::Destroy(
  7745. VOID
  7746. )
  7747. {
  7748. if (NULL != m_hwndParent && !IsWindowEnabled(m_hwndParent))
  7749. {
  7750. EnableWindow(m_hwndParent, TRUE);
  7751. }
  7752. m_dlgProgress.Destroy();
  7753. //
  7754. // Update the view's "Undo" menu and toolbar button based on the current
  7755. // contents of the undo list.
  7756. //
  7757. m_DV.EnableMenuItem_Undo(0 != m_DV.m_pUndoList->Count());
  7758. }
  7759. ///////////////////////////////////////////////////////////////////////////////
  7760. /* Function: DetailsView::Importer::Import [IDataObject *]
  7761. Description: Imports user quota records given an IDataObject pointer.
  7762. Called from DetailsView::Drop().
  7763. Arguments:
  7764. pIDataObject - Pointer to IDataObject interface of object containing
  7765. import data.
  7766. Returns:
  7767. Revision History:
  7768. Date Description Programmer
  7769. -------- --------------------------------------------------- ----------
  7770. 05/20/97 Initial creation. BrianAu
  7771. */
  7772. ///////////////////////////////////////////////////////////////////////////////
  7773. HRESULT
  7774. DetailsView::Importer::Import(
  7775. IDataObject *pIDataObject
  7776. )
  7777. {
  7778. HRESULT hResult = NO_ERROR;
  7779. FORMATETC fmt;
  7780. CStgMedium medium;
  7781. //
  7782. // Array to specify the clipboard formats and media types that
  7783. // we can import from. Ordered by preference.
  7784. //
  7785. struct
  7786. {
  7787. CLIPFORMAT fmt;
  7788. DWORD tymed;
  7789. } rgFmtMedia[] = {{ DataObject::m_CF_NtDiskQuotaExport, TYMED_ISTREAM},
  7790. { DataObject::m_CF_NtDiskQuotaExport, TYMED_HGLOBAL},
  7791. { CF_HDROP, TYMED_ISTREAM},
  7792. { CF_HDROP, TYMED_HGLOBAL}};
  7793. DBGASSERT((NULL != pIDataObject));
  7794. hResult = E_FAIL;
  7795. for (INT i = 0; i < ARRAYSIZE(rgFmtMedia); i++)
  7796. {
  7797. //
  7798. // See which of our supported formats/media types the drop
  7799. // source supports.
  7800. //
  7801. DataObject::SetFormatEtc(fmt, rgFmtMedia[i].fmt, rgFmtMedia[i].tymed);
  7802. //
  7803. // NOTE: I wanted to call QueryGetData to verify a source's support
  7804. // for a format. However, it didn't work properly when
  7805. // pasting an HDROP from the shell. Calling GetData()
  7806. // directly results in the proper behavior.
  7807. //
  7808. hResult = pIDataObject->GetData(&fmt, &medium);
  7809. if (SUCCEEDED(hResult))
  7810. {
  7811. break;
  7812. }
  7813. }
  7814. if (SUCCEEDED(hResult))
  7815. {
  7816. //
  7817. // Successfully have dropped data from the source.
  7818. // Import users from it.
  7819. //
  7820. hResult = Import(fmt, medium);
  7821. }
  7822. else
  7823. {
  7824. DBGERROR((TEXT("PasteFromData: Error 0x%08X, Drop source doesn't support our format/media"), hResult));
  7825. }
  7826. return hResult;
  7827. }
  7828. ///////////////////////////////////////////////////////////////////////////////
  7829. /* Function: DetailsView::Importer::Import [FORMATETC&, STGMEDIUM&]
  7830. Description: Imports one or more users from a storage medium.
  7831. Arguments:
  7832. fmt - Reference to the FORMATETC structure describing the data format.
  7833. medium - Reference to the STGMEDIUM structure describing the medium.
  7834. Returns:
  7835. Revision History:
  7836. Date Description Programmer
  7837. -------- --------------------------------------------------- ----------
  7838. 05/20/97 Initial creation. BrianAu
  7839. */
  7840. ///////////////////////////////////////////////////////////////////////////////
  7841. HRESULT
  7842. DetailsView::Importer::Import(
  7843. const FORMATETC& fmt,
  7844. const STGMEDIUM& medium
  7845. )
  7846. {
  7847. HRESULT hResult = E_FAIL;
  7848. IStream *pIStream = NULL;
  7849. if (TYMED_HGLOBAL == medium.tymed)
  7850. {
  7851. //
  7852. // Medium type is an HGLOBAL but our import functions need
  7853. // a stream.
  7854. //
  7855. hResult = CreateStreamOnHGlobal(medium.hGlobal, FALSE, &pIStream);
  7856. }
  7857. else if (TYMED_ISTREAM == medium.tymed)
  7858. {
  7859. pIStream = medium.pstm;
  7860. hResult = NO_ERROR;
  7861. }
  7862. //
  7863. // OK. The source can render data in one of our acceptable
  7864. // formats and medium types. Go ahead and have them render
  7865. // it onto our stream.
  7866. //
  7867. if (NULL != pIStream)
  7868. {
  7869. if (DetailsView::DataObject::m_CF_NtDiskQuotaExport == fmt.cfFormat)
  7870. {
  7871. //
  7872. // Stream contains quota record information directly.
  7873. // Import the records.
  7874. //
  7875. Source src(pIStream);
  7876. Import(src);
  7877. }
  7878. else if (CF_HDROP == fmt.cfFormat)
  7879. {
  7880. //
  7881. // Stream contains names of files that potentially
  7882. // contain quota record information.
  7883. //
  7884. HGLOBAL hDrop;
  7885. hResult = GetHGlobalFromStream(pIStream, &hDrop);
  7886. if (SUCCEEDED(hResult))
  7887. {
  7888. hResult = Import((HDROP)hDrop);
  7889. }
  7890. }
  7891. }
  7892. else
  7893. {
  7894. DBGERROR((TEXT("PasteFromData: GetData failed with error 0x%08X"), hResult));
  7895. }
  7896. return hResult;
  7897. }
  7898. ///////////////////////////////////////////////////////////////////////////////
  7899. /* Function: DetailsView::Importer::Import [LPCTSTR]
  7900. Description: Imports settings for one or more users from a doc file on
  7901. disk. The doc file contains the import data directly in the stream.
  7902. After opening and validating the storage and stream, it passes the
  7903. stream to ImportUsersFromStream.
  7904. Arguments:
  7905. pszFilePath - Path to doc file containing import information stream.
  7906. bUserCancelled - Reference to variable that is returned status
  7907. indicating if the user cancelled the import operation.
  7908. Returns:
  7909. NO_ERROR = Success.
  7910. S_FALSE = Not a doc file.
  7911. STG_E_FILENOTFOUND
  7912. STG_E_OUTOFMEMORY
  7913. STG_E_ACCESSDENIED
  7914. STG_E_INVALIDNAME
  7915. STG_E_TOOMANYOPENFILES
  7916. Revision History:
  7917. Date Description Programmer
  7918. -------- --------------------------------------------------- ----------
  7919. 05/20/97 Initial creation. BrianAu
  7920. */
  7921. ///////////////////////////////////////////////////////////////////////////////
  7922. HRESULT
  7923. DetailsView::Importer::Import(
  7924. LPCTSTR pszFilePath
  7925. )
  7926. {
  7927. HRESULT hResult = NO_ERROR;
  7928. BOOL bStreamFailure = FALSE; // FALSE = Storage failure.
  7929. //
  7930. // Display the filename in the progress dialog.
  7931. //
  7932. m_dlgProgress.SetFileName(pszFilePath);
  7933. //
  7934. // Validate and open the file.
  7935. //
  7936. if (S_OK != StgIsStorageFile(pszFilePath))
  7937. {
  7938. //
  7939. // Not a doc file. Assume it was created using drag/drop.
  7940. // Map the file into memory and import from that.
  7941. // Contents will be validated during the import process.
  7942. //
  7943. MappedFile file;
  7944. hResult = file.Open(pszFilePath);
  7945. if (SUCCEEDED(hResult))
  7946. {
  7947. //
  7948. // This typecast from __int64 to ULONG is OK. Truncation
  7949. // will not be a problem. There will be no quota import
  7950. // storages larger than 4GB.
  7951. //
  7952. Source src(file.Base(), (ULONG)file.Size());
  7953. hResult = Import(src);
  7954. }
  7955. }
  7956. else
  7957. {
  7958. IStorage *pStg = NULL;
  7959. //
  7960. // It's a doc file. Assume it's one created using OnCmdExport().
  7961. // Contents will be validated during the import process.
  7962. //
  7963. hResult = StgOpenStorage(pszFilePath,
  7964. NULL,
  7965. STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
  7966. NULL, 0,
  7967. &pStg);
  7968. if (SUCCEEDED(hResult))
  7969. {
  7970. //
  7971. // Open the import stream.
  7972. //
  7973. IStream *pIStream;
  7974. hResult = pStg->OpenStream(DetailsView::DataObject::SZ_EXPORT_STREAM_NAME,
  7975. NULL,
  7976. STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
  7977. 0,
  7978. &pIStream);
  7979. if (SUCCEEDED(hResult))
  7980. {
  7981. //
  7982. // Import information contained in the stream.
  7983. //
  7984. Source src(pIStream);
  7985. hResult = Import(src);
  7986. pIStream->Release();
  7987. }
  7988. else
  7989. {
  7990. DBGERROR((TEXT("Import: Error 0x%08X opening stream \"%s\""), hResult, DataObject::SZ_EXPORT_STREAM_NAME));
  7991. //
  7992. // Reporting logic below needs to know if it was a stream or storage
  7993. // failure.
  7994. //
  7995. bStreamFailure = TRUE;
  7996. }
  7997. pStg->Release();
  7998. }
  7999. else
  8000. {
  8001. DBGERROR((TEXT("Import: Error 0x%08X opening storage \"%s\""), hResult, pszFilePath));
  8002. }
  8003. }
  8004. if (FAILED(hResult))
  8005. {
  8006. UINT iMsg = IDS_IMPORT_STREAM_READ_ERROR; // Generic message.
  8007. switch(hResult)
  8008. {
  8009. case STG_E_FILENOTFOUND:
  8010. //
  8011. // Both OpenStream and StgOpenStorage can return
  8012. // STG_E_FILENOTFOUND. However, they have two completely
  8013. // different meanings from the user's perspective.
  8014. //
  8015. iMsg = bStreamFailure ? IDS_IMPORT_STREAM_INVALID_STREAM :
  8016. IDS_IMPORT_STREAM_FILENOTFOUND;
  8017. break;
  8018. case STG_E_ACCESSDENIED:
  8019. iMsg = IDS_IMPORT_STREAM_NOACCESS;
  8020. break;
  8021. case E_OUTOFMEMORY:
  8022. case STG_E_INSUFFICIENTMEMORY:
  8023. iMsg = IDS_IMPORT_STREAM_OUTOFMEMORY;
  8024. break;
  8025. case STG_E_INVALIDNAME:
  8026. iMsg = IDS_IMPORT_STREAM_INVALIDNAME;
  8027. break;
  8028. case STG_E_TOOMANYOPENFILES:
  8029. iMsg = IDS_IMPORT_STREAM_TOOMANYFILES;
  8030. break;
  8031. default:
  8032. break;
  8033. }
  8034. DiskQuotaMsgBox(GetTopmostWindow(),
  8035. iMsg,
  8036. IDS_TITLE_DISK_QUOTA,
  8037. MB_ICONERROR | MB_OK);
  8038. }
  8039. return hResult;
  8040. }
  8041. ///////////////////////////////////////////////////////////////////////////////
  8042. /* Function: DetailsView::Importer::Import [HDROP]
  8043. Description: Imports settings from one or more doc files specified
  8044. in a DROPFILES buffer. This is used when someone drops an export
  8045. file onto the listview. The doc file names are extracted from
  8046. the HDROP buffer then handed off to ImportUsersFromFile.
  8047. Arguments:
  8048. pIStream - Pointer to IStream containing DROPFILES info.
  8049. bUserCancelled - Reference to variable that is returned status
  8050. indicating if the user cancelled the import operation.
  8051. Returns:
  8052. Revision History:
  8053. Date Description Programmer
  8054. -------- --------------------------------------------------- ----------
  8055. 05/20/97 Initial creation. BrianAu
  8056. */
  8057. ///////////////////////////////////////////////////////////////////////////////
  8058. HRESULT
  8059. DetailsView::Importer::Import(
  8060. HDROP hDrop
  8061. )
  8062. {
  8063. HRESULT hResult = NO_ERROR;
  8064. TCHAR szFile[MAX_PATH];
  8065. DBGASSERT((NULL != hDrop));
  8066. //
  8067. // Get the count of files in the HDROP buffer.
  8068. //
  8069. UINT cFiles = DragQueryFile((HDROP)hDrop, (UINT)-1, NULL, 0);
  8070. if ((UINT)-1 != cFiles)
  8071. {
  8072. //
  8073. // Import users from each file in the HDROP buffer.
  8074. // Bail out if user cancels operation.
  8075. //
  8076. for (INT i = 0; i < (INT)cFiles && !m_bUserCancelled; i++)
  8077. {
  8078. DragQueryFile(hDrop, i, szFile, ARRAYSIZE(szFile));
  8079. hResult = Import(szFile);
  8080. }
  8081. }
  8082. else
  8083. {
  8084. DBGERROR((TEXT("DragQueryFile returned -1")));
  8085. }
  8086. return hResult;
  8087. }
  8088. ///////////////////////////////////////////////////////////////////////////////
  8089. /* Function: DetailsView::Importer::Import [Source&]
  8090. Description: Imports settings for one or more users from a Source object.
  8091. All import functions eventually get their information into a Source
  8092. object format and call this function. It then separates out the
  8093. individual user information and calls ImportOneUser() to do the
  8094. actual import.
  8095. Arguments:
  8096. source - Reference to Source containing user import info.
  8097. Returns: Number of users imported.
  8098. Revision History:
  8099. Date Description Programmer
  8100. -------- --------------------------------------------------- ----------
  8101. 05/20/97 Initial creation. BrianAu
  8102. */
  8103. ///////////////////////////////////////////////////////////////////////////////
  8104. HRESULT
  8105. DetailsView::Importer::Import(
  8106. Source& source
  8107. )
  8108. {
  8109. ULONG cbRead;
  8110. HRESULT hResult = E_FAIL;
  8111. try
  8112. {
  8113. //
  8114. // Read and validate the stream signature.
  8115. // This signature consists of a GUID so that we can validate any
  8116. // stream used for import of quota information.
  8117. //
  8118. GUID guidStreamSignature;
  8119. if (S_OK != source.Read(&guidStreamSignature, sizeof(guidStreamSignature), &cbRead))
  8120. throw CFileException(CFileException::read, TEXT(""), 0);
  8121. if (guidStreamSignature == GUID_NtDiskQuotaStream)
  8122. {
  8123. //
  8124. // Read and validate the stream version.
  8125. // Currently there is only 1 version of stream generated so validation
  8126. // is simple. If we ever rev the stream format and bump the version
  8127. // to 2, we should still be able to handle version 1 streams. The
  8128. // only reason to display an error is if we encounter a totally bogus
  8129. // stream version number.
  8130. //
  8131. DWORD nVersion;
  8132. if (S_OK != source.Read(&nVersion, sizeof(nVersion), &cbRead))
  8133. throw CFileException(CFileException::read, TEXT(""), 0);
  8134. if (1 == nVersion)
  8135. {
  8136. INT cRecords;
  8137. //
  8138. // Read the count of records in the stream.
  8139. //
  8140. if (S_OK != source.Read(&cRecords, sizeof(cRecords), &cbRead))
  8141. throw CFileException(CFileException::read, TEXT(""), 0);
  8142. //
  8143. // Set up the progress bar to represent this stream.
  8144. //
  8145. m_dlgProgress.ProgressBarInit(0, cRecords, 1);
  8146. for (INT i = 0; !m_bUserCancelled && i < cRecords; i++)
  8147. {
  8148. //
  8149. // Read each record from the stream.
  8150. // A record consists of a SID-Length value followed by a SID
  8151. // then followed by the user's quota amount used, threshold
  8152. // and limit values. Abort loop if user cancels the
  8153. // operation.
  8154. //
  8155. DWORD cbSid;
  8156. LPBYTE pbSid;
  8157. if (S_OK != source.Read(&cbSid, sizeof(cbSid), &cbRead))
  8158. throw CFileException(CFileException::read, TEXT(""), 0);
  8159. pbSid = new BYTE[cbSid];
  8160. try
  8161. {
  8162. if (NULL != pbSid)
  8163. {
  8164. PDISKQUOTA_USER pIUser = NULL;
  8165. LONGLONG llQuotaThreshold;
  8166. LONGLONG llQuotaLimit;
  8167. //
  8168. // Read in the user's SID.
  8169. //
  8170. if (S_OK != source.Read(pbSid, cbSid, &cbRead))
  8171. throw CFileException(CFileException::read, TEXT(""), 0);
  8172. //
  8173. // Read in the user's quota amount used.
  8174. // This isn't used in the import process but it's in
  8175. // the stream. Therefore we just dump it into the
  8176. // threshold buffer. It will be overwritten.
  8177. //
  8178. if (S_OK != source.Read(&llQuotaThreshold, sizeof(llQuotaThreshold), &cbRead))
  8179. throw CFileException(CFileException::read, TEXT(""), 0);
  8180. //
  8181. // Read in the user's quota threshold.
  8182. //
  8183. if (S_OK != source.Read(&llQuotaThreshold, sizeof(llQuotaThreshold), &cbRead))
  8184. throw CFileException(CFileException::read, TEXT(""), 0);
  8185. //
  8186. // Read in the user's quota limit.
  8187. //
  8188. if (S_OK != source.Read(&llQuotaLimit, sizeof(llQuotaLimit), &cbRead))
  8189. throw CFileException(CFileException::read, TEXT(""), 0);
  8190. //
  8191. // We have one record of data for a user.
  8192. // Now import it.
  8193. //
  8194. hResult = Import(pbSid, llQuotaThreshold, llQuotaLimit);
  8195. delete[] pbSid;
  8196. }
  8197. }
  8198. catch(CFileException& fe)
  8199. {
  8200. DBGERROR((TEXT("Import: File exception caught while reading import data.")));
  8201. delete[] pbSid;
  8202. throw;
  8203. }
  8204. catch(CAllocException& ae)
  8205. {
  8206. DBGERROR((TEXT("Import: Alloc exception caught while reading import data.")));
  8207. delete[] pbSid;
  8208. throw;
  8209. }
  8210. }
  8211. }
  8212. else
  8213. {
  8214. //
  8215. // Invalid stream version.
  8216. // Our code should always be able to handle any version
  8217. // we produce. This code branch should only handle BOGUS
  8218. // version numbers. In other words, a message like "Can't
  8219. // understand this version" is not acceptable.
  8220. //
  8221. DBGERROR((TEXT("Import: Invalid stream version (%d)."), nVersion));
  8222. DiskQuotaMsgBox(GetTopmostWindow(),
  8223. IDS_IMPORT_STREAM_INVALID_STREAM,
  8224. IDS_TITLE_DISK_QUOTA,
  8225. MB_ICONERROR | MB_OK);
  8226. }
  8227. }
  8228. else
  8229. {
  8230. //
  8231. // Invalid stream signature.
  8232. //
  8233. DBGERROR((TEXT("Import: Invalid stream signature.")));
  8234. DiskQuotaMsgBox(GetTopmostWindow(),
  8235. IDS_IMPORT_STREAM_INVALID_STREAM,
  8236. IDS_TITLE_DISK_QUOTA,
  8237. MB_ICONERROR | MB_OK);
  8238. }
  8239. }
  8240. catch(CFileException& fe)
  8241. {
  8242. DBGERROR((TEXT("Import: File exception caught while reading import data.")));
  8243. DiskQuotaMsgBox(GetTopmostWindow(),
  8244. IDS_IMPORT_STREAM_READ_ERROR,
  8245. IDS_TITLE_DISK_QUOTA,
  8246. MB_ICONERROR | MB_OK);
  8247. hResult = HRESULT_FROM_WIN32(ERROR_READ_FAULT);
  8248. }
  8249. return hResult;
  8250. }
  8251. ///////////////////////////////////////////////////////////////////////////////
  8252. /* Function: DetailsView::Importer::Import [LPBYTE, LONGLONG, LONGLONG]
  8253. Description: Imports a single user into the system given the user's SID
  8254. and quota settings. This is the single function where all import
  8255. mechanisms end up. It does the actual importing of the user.
  8256. Arguments:
  8257. pbSid - Address of buffer containing user's SID.
  8258. llQuotaThreshold - User's quota warning threshold setting.
  8259. llQuotaLimit - User's quota limit setting.
  8260. Returns:
  8261. -1 = User pressed "Cancel" in either the "Replace User" dialog or
  8262. in the progress dialog.
  8263. 0 = Failed to import user.
  8264. 1 = User imported.
  8265. Revision History:
  8266. Date Description Programmer
  8267. -------- --------------------------------------------------- ----------
  8268. 05/20/97 Initial creation. BrianAu
  8269. */
  8270. ///////////////////////////////////////////////////////////////////////////////
  8271. HRESULT
  8272. DetailsView::Importer::Import(
  8273. LPBYTE pbSid,
  8274. LONGLONG llQuotaThreshold,
  8275. LONGLONG llQuotaLimit
  8276. )
  8277. {
  8278. INT iResult = 0;
  8279. HRESULT hResult = NO_ERROR;
  8280. PDISKQUOTA_USER pIUser = NULL;
  8281. static BOOL bReplaceExistingUser = FALSE;
  8282. DBGASSERT((NULL != pbSid));
  8283. if (m_bPromptOnReplace)
  8284. {
  8285. //
  8286. // We'll be prompting the user if a record needs replacement.
  8287. // They'll make a choice through the UI.
  8288. // Assume for now that we won't be replacing the record.
  8289. //
  8290. bReplaceExistingUser = FALSE;
  8291. }
  8292. //
  8293. // Add user to volume's quota file.
  8294. //
  8295. hResult = m_DV.m_pQuotaControl->AddUserSid(pbSid,
  8296. DISKQUOTA_USERNAME_RESOLVE_SYNC,
  8297. &pIUser);
  8298. if (SUCCEEDED(hResult))
  8299. {
  8300. //
  8301. // Either the user was added or already exists.
  8302. //
  8303. BOOL bAddNewUser = (S_FALSE != hResult);
  8304. if (!bAddNewUser)
  8305. {
  8306. //
  8307. // User already exists in the quota file. Find it's entry
  8308. // in the listview.
  8309. //
  8310. DBGASSERT((NULL != pIUser));
  8311. pIUser->Release();
  8312. INT iItem = m_DV.FindUserBySid(pbSid, &pIUser);
  8313. if (m_bPromptOnReplace)
  8314. {
  8315. TCHAR szLogonName[MAX_USERNAME] = { TEXT('\0') };
  8316. TCHAR szDisplayName[MAX_USERNAME] = { TEXT('\0') };
  8317. if (-1 != iItem)
  8318. {
  8319. //
  8320. // Listview item found.
  8321. // Get the account's name string so we can ask the user
  8322. // if they want to replace it's quota settings.
  8323. //
  8324. DBGASSERT((NULL != pIUser));
  8325. pIUser->GetName(NULL, 0,
  8326. szLogonName, ARRAYSIZE(szLogonName),
  8327. szDisplayName, ARRAYSIZE(szDisplayName));
  8328. }
  8329. CString strTitle(g_hInstDll, IDS_TITLE_DISK_QUOTA);
  8330. CString strMsg(g_hInstDll,
  8331. IDS_IMPORT_REPLACE_RECORD,
  8332. szDisplayName,
  8333. szLogonName);
  8334. //
  8335. // Ask the user if they want to replace the record's
  8336. // quota settings.
  8337. //
  8338. YesNoToAllDialog ynToAllDlg(IDD_YNTOALL);
  8339. INT_PTR iResponse = ynToAllDlg.CreateAndRun(g_hInstDll,
  8340. GetTopmostWindow(),
  8341. strTitle,
  8342. strMsg);
  8343. //
  8344. // If the "Apply to All" checkbox was selected, we set this flag
  8345. // so that the dialog isn't displayed again until the caller resets
  8346. // m_bPromptOnReplace to TRUE.
  8347. //
  8348. m_bPromptOnReplace = !ynToAllDlg.ApplyToAll();
  8349. switch(iResponse)
  8350. {
  8351. case IDYES:
  8352. bReplaceExistingUser = TRUE;
  8353. break;
  8354. case IDCANCEL:
  8355. m_bUserCancelled = TRUE;
  8356. break;
  8357. default:
  8358. break;
  8359. }
  8360. }
  8361. }
  8362. if (bAddNewUser || bReplaceExistingUser)
  8363. {
  8364. DBGASSERT((NULL != pIUser));
  8365. //
  8366. // Write the new quota values because...
  8367. //
  8368. // 1. Added a new user record and setting initial values or...
  8369. // 2. Replacing settings for an existing user.
  8370. //
  8371. if (NULL != pIUser)
  8372. {
  8373. LONGLONG llQuotaThresholdUndo;
  8374. LONGLONG llQuotaLimitUndo;
  8375. if (!bAddNewUser && bReplaceExistingUser)
  8376. {
  8377. //
  8378. // Save the current threshold and limit values for "undo".
  8379. // Only need information for undo if replacing an existing
  8380. // user's settings. For performance, only call when we need
  8381. // the info.
  8382. //
  8383. pIUser->GetQuotaThreshold(&llQuotaThresholdUndo);
  8384. pIUser->GetQuotaLimit(&llQuotaLimitUndo);
  8385. }
  8386. //
  8387. // Set the new threshold and limit values.
  8388. //
  8389. pIUser->SetQuotaThreshold(llQuotaThreshold, TRUE);
  8390. pIUser->SetQuotaLimit(llQuotaLimit, TRUE);
  8391. if (bAddNewUser)
  8392. {
  8393. //
  8394. // Add the user to the listview and create an UNDO object for the operation.
  8395. //
  8396. m_DV.AddUser(pIUser);
  8397. pIUser->AddRef();
  8398. m_DV.m_pQuotaControl->AddRef();
  8399. autoptr<UndoAdd> ptrUndoAdd = new UndoAdd(pIUser, m_DV.m_pQuotaControl);
  8400. m_DV.m_pUndoList->Add(ptrUndoAdd);
  8401. ptrUndoAdd.disown();
  8402. }
  8403. if (!bAddNewUser && bReplaceExistingUser)
  8404. {
  8405. //
  8406. // This will update the record to display any changed quota values.
  8407. // Create an UNDO object for the operation.
  8408. //
  8409. m_DV.OnUserNameChanged(pIUser);
  8410. pIUser->AddRef();
  8411. autoptr<UndoModify> ptrUndoModify = new UndoModify(pIUser, llQuotaThresholdUndo, llQuotaLimitUndo);
  8412. m_DV.m_pUndoList->Add(ptrUndoModify);
  8413. ptrUndoModify.disown();
  8414. }
  8415. }
  8416. }
  8417. }
  8418. if (!m_bUserCancelled)
  8419. m_bUserCancelled = m_dlgProgress.UserCancelled();
  8420. if (SUCCEEDED(hResult))
  8421. {
  8422. m_cImported++;
  8423. m_dlgProgress.ProgressBarAdvance();
  8424. }
  8425. return hResult;
  8426. }
  8427. ///////////////////////////////////////////////////////////////////////////////
  8428. /* Function: DetailsView::Importer::GetTopmostWindow
  8429. Description: Returns the HWND of the topmost window in the importer UI.
  8430. If the UI's progress dialog is visible, the dialog's HWND is returned.
  8431. Otherwise, the value of m_hwndParent is returned.
  8432. The Importer uses this function to identify what window should be parent
  8433. to any error message boxes.
  8434. Arguments: None.
  8435. Returns: HWND to use for parent of any messages boxes created by the
  8436. Importer.
  8437. Revision History:
  8438. Date Description Programmer
  8439. -------- --------------------------------------------------- ----------
  8440. 05/20/97 Initial creation. BrianAu
  8441. */
  8442. ///////////////////////////////////////////////////////////////////////////////
  8443. HWND
  8444. DetailsView::Importer::GetTopmostWindow(
  8445. VOID
  8446. )
  8447. {
  8448. return m_dlgProgress.m_hWnd ? m_dlgProgress.m_hWnd : m_hwndParent;
  8449. }
  8450. ///////////////////////////////////////////////////////////////////////////////
  8451. // The following StreamSource functions implement a layer of abstraction
  8452. // between the import function and the source of the import data. This allows
  8453. // me to centralize the actual import processing in a single function
  8454. // without consideration of input source.
  8455. // There are several Import() overloads but they eventually all call down
  8456. // to Import(Source&). These functions are very simple so I'm not going
  8457. // go bother documenting each. I think it's pretty obvious what they do.
  8458. // Do note the use of the virtual constructor technique allowing the user
  8459. // to deal only with Source objects and not AnySource, StreamSource or
  8460. // MemorySource objects. This may be unfamiliar to some.
  8461. //
  8462. // [brianau 7/25/97]
  8463. //
  8464. ///////////////////////////////////////////////////////////////////////////////
  8465. //
  8466. // Source ---------------------------------------------------------------------
  8467. //
  8468. DetailsView::Importer::Source::Source(
  8469. IStream *pStm
  8470. ) : m_pTheSource(NULL)
  8471. {
  8472. //
  8473. // Create a stream source type object.
  8474. //
  8475. m_pTheSource = new StreamSource(pStm);
  8476. }
  8477. DetailsView::Importer::Source::Source(
  8478. LPBYTE pb,
  8479. ULONG cbMax
  8480. ) : m_pTheSource(NULL)
  8481. {
  8482. //
  8483. // Create a memory source type object.
  8484. //
  8485. m_pTheSource = new MemorySource(pb, cbMax);
  8486. }
  8487. DetailsView::Importer::Source::~Source(
  8488. VOID
  8489. )
  8490. {
  8491. //
  8492. // Note: Destructors must be virtual for this to work.
  8493. //
  8494. delete m_pTheSource;
  8495. }
  8496. HRESULT
  8497. DetailsView::Importer::Source::Read(
  8498. LPVOID pvOut,
  8499. ULONG cb,
  8500. ULONG *pcbRead
  8501. )
  8502. {
  8503. HRESULT hr = E_OUTOFMEMORY;
  8504. if (NULL != m_pTheSource)
  8505. {
  8506. //
  8507. // Delegate the read operation to the properly-typed
  8508. // subobject.
  8509. //
  8510. hr = m_pTheSource->Read(pvOut, cb, pcbRead);
  8511. }
  8512. return hr;
  8513. }
  8514. //
  8515. // StreamSource ---------------------------------------------------------------
  8516. //
  8517. DetailsView::Importer::StreamSource::StreamSource(
  8518. IStream *pStm
  8519. ) : m_pStm(pStm)
  8520. {
  8521. //
  8522. // AddRef the stream pointer.
  8523. //
  8524. if (NULL != m_pStm)
  8525. m_pStm->AddRef();
  8526. }
  8527. DetailsView::Importer::StreamSource::~StreamSource(
  8528. VOID
  8529. )
  8530. {
  8531. //
  8532. // Release the stream pointer.
  8533. //
  8534. if (NULL != m_pStm)
  8535. m_pStm->Release();
  8536. }
  8537. HRESULT
  8538. DetailsView::Importer::StreamSource::Read(
  8539. LPVOID pvOut,
  8540. ULONG cb,
  8541. ULONG *pcbRead
  8542. )
  8543. {
  8544. HRESULT hr = E_FAIL;
  8545. if (NULL != m_pStm)
  8546. {
  8547. //
  8548. // Read data from the stream.
  8549. //
  8550. hr = m_pStm->Read(pvOut, cb, pcbRead);
  8551. }
  8552. return hr;
  8553. }
  8554. //
  8555. // MemorySource ---------------------------------------------------------------
  8556. //
  8557. DetailsView::Importer::MemorySource::MemorySource(
  8558. LPBYTE pb,
  8559. ULONG cbMax
  8560. ) : m_pb(pb),
  8561. m_cbMax(cbMax)
  8562. {
  8563. }
  8564. HRESULT
  8565. DetailsView::Importer::MemorySource::Read(
  8566. LPVOID pvOut,
  8567. ULONG cb,
  8568. ULONG *pcbRead
  8569. )
  8570. {
  8571. HRESULT hr = E_FAIL;
  8572. if (m_cbMax >= cb)
  8573. {
  8574. //
  8575. // Read data from the memory block.
  8576. //
  8577. CopyMemory(pvOut, m_pb, cb);
  8578. m_pb += cb;
  8579. m_cbMax -= cb;
  8580. if (NULL != pcbRead)
  8581. {
  8582. *pcbRead = cb;
  8583. }
  8584. hr = S_OK;
  8585. }
  8586. return hr;
  8587. }