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

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