Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

800 lines
17 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. progbar.c
  5. Abstract:
  6. Centralizes access to the progress bar and associated messages accross components
  7. (hwcomp,migapp,etc.) and sides (w9x, nt)
  8. Author:
  9. Marc R. Whitten (marcw) 14-Apr-1997
  10. Revision History:
  11. jimschm 19-Jun-1998 Improved to allow revision of estimates, necessary
  12. for NT-side progress bar.
  13. --*/
  14. #include "pch.h"
  15. //
  16. // Types
  17. //
  18. typedef struct {
  19. BOOL Started;
  20. BOOL Completed;
  21. UINT InitialGuess;
  22. UINT TotalTicks;
  23. UINT TicksSoFar;
  24. UINT LastTickDisplayed;
  25. } SLICE, *PSLICE;
  26. typedef struct {
  27. HWND Window;
  28. HANDLE CancelEvent;
  29. PCSTR Message;
  30. DWORD MessageId;
  31. DWORD Delay;
  32. BOOL InUse;
  33. } DELAYTHREADPARAMS, *PDELAYTHREADPARAMS;
  34. #if 0
  35. typedef struct {
  36. HANDLE CancelEvent;
  37. DWORD TickCount;
  38. BOOL InUse;
  39. } TICKTHREADPARAMS, *PTICKTHREADPARAMS;
  40. #endif
  41. //
  42. // Globals
  43. //
  44. BOOL g_ProgBarInitialized = FALSE;
  45. HWND g_ProgressBar;
  46. HWND g_Component;
  47. HWND g_SubComponent;
  48. PBRANGE g_OrgRange;
  49. HANDLE g_ComponentCancelEvent;
  50. HANDLE g_SubComponentCancelEvent;
  51. BOOL *g_CancelFlagPtr;
  52. GROWBUFFER g_SliceArray;
  53. UINT g_SliceCount;
  54. UINT g_MaxTickCount;
  55. UINT g_PaddingTicks;
  56. UINT g_CurrentTickCount;
  57. UINT g_CurrentPos;
  58. UINT g_ReduceFactor;
  59. BOOL g_Reverse = FALSE;
  60. OUR_CRITICAL_SECTION g_ProgBarCriticalSection;
  61. UINT g_CurrentSliceId = (UINT)-1;
  62. //
  63. // Tuning constants
  64. //
  65. #define TICKSCALE 100
  66. //
  67. // Implementaiton
  68. //
  69. VOID
  70. InitializeProgressBar (
  71. IN HWND ProgressBar,
  72. IN HWND Component, OPTIONAL
  73. IN HWND SubComponent, OPTIONAL
  74. IN BOOL *CancelFlagPtr OPTIONAL
  75. )
  76. {
  77. LONG rc;
  78. CHAR Data[256];
  79. DWORD Size;
  80. HKEY Key;
  81. g_ProgressBar = ProgressBar;
  82. g_CancelFlagPtr = CancelFlagPtr;
  83. g_ProgBarInitialized = TRUE;
  84. SendMessage (ProgressBar, PBM_SETPOS, 0, 0);
  85. g_CurrentPos = 0;
  86. SendMessage (ProgressBar, PBM_GETRANGE, 0, (LPARAM) &g_OrgRange);
  87. //
  88. // Create cancel events for delayed messages.
  89. //
  90. g_ComponentCancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  91. g_SubComponentCancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  92. if (!g_ComponentCancelEvent || !g_SubComponentCancelEvent) {
  93. DEBUGMSG ((DBG_ERROR, "ProgressBar: Could not create cancel events."));
  94. }
  95. InitializeOurCriticalSection (&g_ProgBarCriticalSection);
  96. g_Component = Component;
  97. g_SubComponent = SubComponent;
  98. DEBUGMSG_IF ((
  99. Component && !IsWindow (Component),
  100. DBG_WHOOPS,
  101. "Progress bar component is not a valid window"
  102. ));
  103. DEBUGMSG_IF ((
  104. SubComponent && !IsWindow (SubComponent),
  105. DBG_WHOOPS,
  106. "Progress bar sub component is not a valid window"
  107. ));
  108. MYASSERT (!g_SliceCount);
  109. MYASSERT (!g_SliceArray.Buf);
  110. MYASSERT (!g_MaxTickCount);
  111. MYASSERT (!g_PaddingTicks);
  112. MYASSERT (!g_CurrentTickCount);
  113. MYASSERT (g_CurrentSliceId == (UINT)-1);
  114. g_ReduceFactor = 1;
  115. Key = OpenRegKeyStrA ("HKLM\\inapoi");
  116. if (Key) {
  117. Size = 256;
  118. rc = RegQueryValueExA (Key, "", NULL, NULL, (PBYTE) Data, &Size);
  119. CloseRegKey (Key);
  120. if (rc == ERROR_SUCCESS && !lstrcmpiA (Data, "backwards")) {
  121. g_Reverse = TRUE;
  122. }
  123. }
  124. }
  125. VOID
  126. TerminateProgressBar (
  127. VOID
  128. )
  129. {
  130. if (g_ComponentCancelEvent) {
  131. CloseHandle (g_ComponentCancelEvent);
  132. g_ComponentCancelEvent = NULL;
  133. }
  134. if (g_SubComponentCancelEvent) {
  135. CloseHandle (g_SubComponentCancelEvent);
  136. g_SubComponentCancelEvent = NULL;
  137. }
  138. DeleteOurCriticalSection (&g_ProgBarCriticalSection);
  139. FreeGrowBuffer (&g_SliceArray);
  140. g_SliceCount = 0;
  141. g_MaxTickCount = 0;
  142. g_PaddingTicks = 0;
  143. g_CurrentTickCount = 0;
  144. g_CurrentSliceId = -1;
  145. g_Component = NULL;
  146. g_SubComponent = NULL;
  147. g_ReduceFactor = 1;
  148. SendMessage (g_ProgressBar, PBM_SETRANGE32, g_OrgRange.iLow, g_OrgRange.iHigh);
  149. g_ProgBarInitialized = FALSE;
  150. }
  151. UINT
  152. RegisterProgressBarSlice (
  153. IN UINT InitialEstimate
  154. )
  155. {
  156. PSLICE Slice;
  157. UINT SliceId;
  158. MYASSERT (g_ProgBarInitialized);
  159. if (!g_ProgBarInitialized) {
  160. return 0;
  161. }
  162. SliceId = g_SliceCount;
  163. Slice = (PSLICE) GrowBuffer (&g_SliceArray, sizeof (SLICE));
  164. g_SliceCount++;
  165. Slice->Started = FALSE;
  166. Slice->Completed = FALSE;
  167. Slice->TotalTicks = InitialEstimate * TICKSCALE;
  168. Slice->InitialGuess = Slice->TotalTicks;
  169. Slice->TicksSoFar = 0;
  170. Slice->LastTickDisplayed = 0;
  171. return SliceId;
  172. }
  173. VOID
  174. ReviseSliceEstimate (
  175. IN UINT SliceId,
  176. IN UINT RevisedEstimate
  177. )
  178. {
  179. PSLICE Slice;
  180. MYASSERT (g_ProgBarInitialized);
  181. if (!g_ProgBarInitialized) {
  182. return;
  183. }
  184. if (SliceId >= g_SliceCount) {
  185. DEBUGMSG ((DBG_WHOOPS, "ReviseSliceEstimate: Invalid slice ID %u", SliceId));
  186. return;
  187. }
  188. Slice = (PSLICE) g_SliceArray.Buf + SliceId;
  189. if (!g_CurrentTickCount) {
  190. Slice->TotalTicks = RevisedEstimate;
  191. return;
  192. }
  193. if (Slice->Completed) {
  194. DEBUGMSG ((DBG_WHOOPS, "ReviseSliceEstimate: Can't revise completed slice"));
  195. return;
  196. }
  197. if (Slice->InitialGuess == 0) {
  198. return;
  199. }
  200. RevisedEstimate *= TICKSCALE;
  201. MYASSERT (Slice->TicksSoFar * RevisedEstimate > Slice->TicksSoFar);
  202. MYASSERT (Slice->LastTickDisplayed * RevisedEstimate > Slice->LastTickDisplayed);
  203. Slice->TicksSoFar = (UINT) ((LONGLONG) Slice->TicksSoFar * (LONGLONG) RevisedEstimate / (LONGLONG) Slice->TotalTicks);
  204. Slice->LastTickDisplayed = (UINT) ((LONGLONG) Slice->LastTickDisplayed * (LONGLONG) RevisedEstimate / (LONGLONG) Slice->TotalTicks);
  205. Slice->TotalTicks = RevisedEstimate;
  206. }
  207. VOID
  208. BeginSliceProcessing (
  209. IN UINT SliceId
  210. )
  211. {
  212. PSLICE Slice;
  213. UINT u;
  214. UINT TotalTicks;
  215. MYASSERT (g_ProgBarInitialized);
  216. if (!g_ProgBarInitialized) {
  217. return;
  218. }
  219. if (!g_ProgressBar) {
  220. DEBUGMSG ((DBG_WHOOPS, "No progress bar handle"));
  221. return;
  222. }
  223. if (SliceId >= g_SliceCount) {
  224. DEBUGMSG ((DBG_WHOOPS, "BeginSliceProcessing: Invalid slice ID %u", SliceId));
  225. return;
  226. }
  227. if (!g_CurrentTickCount) {
  228. //
  229. // Initialize the progress bar
  230. //
  231. MYASSERT (g_CurrentSliceId == (UINT)-1);
  232. TotalTicks = 0;
  233. Slice = (PSLICE) g_SliceArray.Buf;
  234. for (u = 0 ; u < g_SliceCount ; u++) {
  235. TotalTicks += Slice->InitialGuess;
  236. Slice++;
  237. }
  238. TotalTicks /= TICKSCALE;
  239. g_PaddingTicks = TotalTicks * 5 / 100;
  240. g_MaxTickCount = TotalTicks + 2 * g_PaddingTicks;
  241. g_ReduceFactor = 1;
  242. while (g_MaxTickCount > 0xffff) {
  243. g_ReduceFactor *= 10;
  244. g_MaxTickCount /= 10;
  245. }
  246. SendMessage (g_ProgressBar, PBM_SETRANGE, 0, MAKELPARAM (0, g_MaxTickCount));
  247. SendMessage (g_ProgressBar, PBM_SETSTEP, 1, 0);
  248. if (g_Reverse) {
  249. SendMessage (
  250. g_ProgressBar,
  251. PBM_SETPOS,
  252. g_MaxTickCount - (g_PaddingTicks / g_ReduceFactor),
  253. 0
  254. );
  255. } else {
  256. SendMessage (g_ProgressBar, PBM_SETPOS, g_PaddingTicks / g_ReduceFactor, 0);
  257. }
  258. g_CurrentTickCount = g_PaddingTicks;
  259. g_CurrentPos = g_PaddingTicks;
  260. } else if (SliceId <= g_CurrentSliceId) {
  261. DEBUGMSG ((DBG_WHOOPS, "BeginSliceProcessing: Slice ID %u processed already", SliceId));
  262. return;
  263. }
  264. g_CurrentSliceId = SliceId;
  265. Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
  266. Slice->Started = TRUE;
  267. }
  268. VOID
  269. pIncrementBarIfNecessary (
  270. IN OUT PSLICE Slice
  271. )
  272. {
  273. UINT Increment;
  274. UINT Pos;
  275. if (Slice->TicksSoFar >= Slice->TotalTicks) {
  276. Slice->TicksSoFar = Slice->TotalTicks;
  277. Slice->Completed = TRUE;
  278. }
  279. if (Slice->TicksSoFar - Slice->LastTickDisplayed >= TICKSCALE) {
  280. Increment = (Slice->TicksSoFar - Slice->LastTickDisplayed) / TICKSCALE;
  281. Slice->LastTickDisplayed += Increment * TICKSCALE;
  282. if (g_Reverse) {
  283. Pos = ((g_CurrentPos + Slice->TicksSoFar) / TICKSCALE);
  284. Pos += g_PaddingTicks;
  285. Pos /= g_ReduceFactor;
  286. if (Pos > g_MaxTickCount) {
  287. Pos = g_MaxTickCount - (g_PaddingTicks / g_ReduceFactor);
  288. }
  289. SendMessage (g_ProgressBar, PBM_SETPOS, g_MaxTickCount - Pos, 0);
  290. } else {
  291. SendMessage (g_ProgressBar, PBM_DELTAPOS, Increment / g_ReduceFactor, 0);
  292. }
  293. }
  294. }
  295. VOID
  296. pTickProgressBar (
  297. IN UINT Ticks
  298. )
  299. {
  300. PSLICE Slice;
  301. LONGLONG x;
  302. if (!Ticks || g_CurrentSliceId == (UINT)-1 || g_CurrentSliceId >= g_SliceCount) {
  303. return;
  304. }
  305. Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
  306. if (!Slice->InitialGuess) {
  307. return;
  308. }
  309. if (Slice->Completed) {
  310. DEBUGMSG ((DBG_VERBOSE, "Progress slice ID %u already completed", g_CurrentSliceId));
  311. return;
  312. }
  313. MYASSERT (Ticks * TICKSCALE > Ticks);
  314. x = ((LONGLONG) Ticks * TICKSCALE * (LONGLONG) Slice->TotalTicks) / (LONGLONG) Slice->InitialGuess;
  315. MYASSERT (x + (LONGLONG) Slice->TicksSoFar < 0x100000000);
  316. Slice->TicksSoFar += (UINT) x;
  317. pIncrementBarIfNecessary (Slice);
  318. }
  319. BOOL
  320. TickProgressBarDelta (
  321. IN UINT TickCount
  322. )
  323. {
  324. BOOL rSuccess = TRUE;
  325. if (!g_ProgBarInitialized) {
  326. return TRUE;
  327. }
  328. if (g_CancelFlagPtr && *g_CancelFlagPtr) {
  329. SetLastError (ERROR_CANCELLED);
  330. rSuccess = FALSE;
  331. } else {
  332. pTickProgressBar (TickCount);
  333. }
  334. return rSuccess;
  335. }
  336. BOOL
  337. TickProgressBar (
  338. VOID
  339. )
  340. {
  341. if (!g_ProgBarInitialized) {
  342. return TRUE;
  343. }
  344. return TickProgressBarDelta (1);
  345. }
  346. VOID
  347. EndSliceProcessing (
  348. VOID
  349. )
  350. {
  351. PSLICE Slice;
  352. if (!g_ProgBarInitialized) {
  353. return;
  354. }
  355. Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
  356. if (!Slice->InitialGuess) {
  357. Slice->Completed = TRUE;
  358. return;
  359. }
  360. if (!Slice->Completed) {
  361. DEBUGMSG ((DBG_WARNING, "Progress bar slice %u was not completed.", g_CurrentSliceId));
  362. Slice->TicksSoFar = Slice->TotalTicks;
  363. Slice->Completed = TRUE;
  364. pIncrementBarIfNecessary (Slice);
  365. }
  366. g_CurrentPos += Slice->TotalTicks;
  367. if (g_CurrentSliceId == g_SliceCount - 1) {
  368. //
  369. // End of progress bar
  370. //
  371. SendMessage (g_ProgressBar, PBM_SETPOS, g_MaxTickCount, 0);
  372. }
  373. }
  374. BOOL
  375. pCheckProgressBarState (
  376. IN HANDLE CancelEvent
  377. )
  378. {
  379. SetEvent(CancelEvent);
  380. return (!g_CancelFlagPtr || !*g_CancelFlagPtr);
  381. }
  382. BOOL
  383. ProgressBar_SetWindowStringA(
  384. IN HWND Window,
  385. IN HANDLE CancelEvent,
  386. IN PCSTR Message, OPTIONAL
  387. IN DWORD MessageId OPTIONAL
  388. )
  389. {
  390. BOOL rSuccess = TRUE;
  391. PCSTR string = NULL;
  392. EnterOurCriticalSection (&g_ProgBarCriticalSection);
  393. if (g_ProgBarInitialized) {
  394. if (pCheckProgressBarState(CancelEvent)) {
  395. if (Message) {
  396. //
  397. // We have a normal message string.
  398. //
  399. if (!SetWindowTextA(Window, Message)) {
  400. rSuccess = FALSE;
  401. DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
  402. }
  403. }
  404. else if (MessageId) {
  405. //
  406. // We have a message ID. Convert it and set it.
  407. //
  408. string = GetStringResourceA(MessageId);
  409. if (string) {
  410. if (!SetWindowTextA(Window, string)) {
  411. rSuccess = FALSE;
  412. DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
  413. }
  414. FreeStringResourceA(string);
  415. }
  416. ELSE_DEBUGMSG((DBG_ERROR,"ProgressBar: Error with GetStringResource"));
  417. }
  418. else {
  419. //
  420. // Just clear the text.
  421. //
  422. if (!SetWindowTextA(Window, "")) {
  423. rSuccess = FALSE;
  424. DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
  425. }
  426. }
  427. }
  428. else {
  429. //
  430. // We are in a canceled state.
  431. //
  432. rSuccess = FALSE;
  433. SetLastError (ERROR_CANCELLED);
  434. }
  435. }
  436. LeaveOurCriticalSection (&g_ProgBarCriticalSection);
  437. return rSuccess;
  438. }
  439. DWORD
  440. pSetDelayedMessageA (
  441. IN PVOID Param
  442. )
  443. {
  444. DWORD rc = ERROR_SUCCESS;
  445. PDELAYTHREADPARAMS tParams = (PDELAYTHREADPARAMS) Param;
  446. //
  447. // Simply wait for the passed in delay or until someone signals the cancel
  448. // event.
  449. //
  450. switch (WaitForSingleObject(tParams -> CancelEvent, tParams -> Delay)) {
  451. case WAIT_TIMEOUT:
  452. //
  453. // We timed out without cancel being signaled. Set the delayed message.
  454. //
  455. ProgressBar_SetWindowStringA (
  456. tParams -> Window,
  457. tParams -> CancelEvent,
  458. tParams -> Message,
  459. tParams -> MessageId
  460. );
  461. break;
  462. case WAIT_OBJECT_0:
  463. default:
  464. //
  465. // We were canceled (or something strange happened :> Do nothing!
  466. //
  467. break;
  468. }
  469. //
  470. // can set a new thread now
  471. //
  472. tParams->InUse = FALSE;
  473. return rc;
  474. }
  475. VOID
  476. ProgressBar_CancelDelayedMessage (
  477. IN HANDLE CancelEvent
  478. )
  479. {
  480. if (!g_ProgBarInitialized) {
  481. return;
  482. }
  483. SetEvent(CancelEvent);
  484. }
  485. BOOL
  486. ProgressBar_SetDelayedMessageA (
  487. IN HWND Window,
  488. IN HANDLE CancelEvent,
  489. IN LPCSTR Message,
  490. IN DWORD MessageId,
  491. IN DWORD Delay
  492. )
  493. {
  494. BOOL rSuccess = FALSE;
  495. DWORD threadId;
  496. static DELAYTHREADPARAMS tParams;
  497. if (!g_ProgBarInitialized || tParams.InUse) {
  498. return TRUE;
  499. }
  500. if (!pCheckProgressBarState(Window)) {
  501. //
  502. // Fill in the parameters for this call to create thread.
  503. //
  504. tParams.Window = Window;
  505. tParams.CancelEvent = CancelEvent;
  506. tParams.Message = Message;
  507. tParams.MessageId = MessageId;
  508. tParams.Delay = Delay;
  509. //
  510. // Spawn off a thread that will set the message.
  511. //
  512. rSuccess = NULL != CreateThread (
  513. NULL, // No inheritance.
  514. 0, // Normal stack size.
  515. pSetDelayedMessageA,
  516. &tParams,
  517. 0, // Run immediately.
  518. &threadId
  519. );
  520. if (rSuccess) {
  521. tParams.InUse = TRUE;
  522. }
  523. ELSE_DEBUGMSG((DBG_ERROR,"Error spawning thread in ProgressBar_SetDelayedMessageA."));
  524. }
  525. return rSuccess;
  526. }
  527. #if 0
  528. DWORD
  529. pTickProgressBarThread (
  530. IN PVOID Param
  531. )
  532. {
  533. DWORD rc = ERROR_SUCCESS;
  534. PTICKTHREADPARAMS Params = (PTICKTHREADPARAMS)Param;
  535. BOOL Continue = TRUE;
  536. //
  537. // Simply wait for the passed in delay or until someone signals the cancel
  538. // event.
  539. //
  540. do {
  541. switch (WaitForSingleObject(Params->CancelEvent, Params->TickCount)) {
  542. case WAIT_TIMEOUT:
  543. //
  544. // We timed out without cancel being signaled. Tick the progress bar.
  545. //
  546. if (!TickProgressBarDelta (Params->TickCount)) {
  547. //
  548. // cancelled
  549. //
  550. Continue = FALSE;
  551. }
  552. break;
  553. case WAIT_OBJECT_0:
  554. default:
  555. //
  556. // We were canceled (or something strange happened :> Do nothing!
  557. //
  558. Continue = FALSE;
  559. break;
  560. }
  561. } while (Continue);
  562. //
  563. // can set a new thread now
  564. //
  565. Params->InUse = FALSE;
  566. return rc;
  567. }
  568. BOOL
  569. ProgressBar_CreateTickThread (
  570. IN HANDLE CancelEvent,
  571. IN DWORD TickCount
  572. )
  573. {
  574. BOOL rSuccess = FALSE;
  575. DWORD threadId;
  576. static TICKTHREADPARAMS g_Params;
  577. if (g_ProgBarInitialized && !g_Params.InUse) {
  578. if (pCheckProgressBarState(NULL)) {
  579. //
  580. // Fill in the parameters for this call to create thread.
  581. //
  582. g_Params.CancelEvent = CancelEvent;
  583. g_Params.TickCount = TickCount;
  584. //
  585. // Spawn off a thread that will set the message.
  586. //
  587. if (CreateThread (
  588. NULL, // No inheritance.
  589. 0, // Normal stack size.
  590. pTickProgressBarThread,
  591. &g_Params,
  592. 0, // Run immediately.
  593. &threadId
  594. )) {
  595. rSuccess = TRUE;
  596. g_Params.InUse = TRUE;
  597. }
  598. ELSE_DEBUGMSG ((DBG_ERROR, "Error spawning thread in ProgressBar_CreateTickThread."));
  599. }
  600. }
  601. return rSuccess;
  602. }
  603. BOOL
  604. ProgressBar_CancelTickThread (
  605. IN HANDLE CancelEvent
  606. )
  607. {
  608. if (!g_ProgBarInitialized) {
  609. return TRUE;
  610. }
  611. return SetEvent(CancelEvent);
  612. }
  613. #endif