windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/common/progbar/progbar.c
2020-09-26 16:20:57 +08:00

801 lines
17 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
progbar.c
Abstract:
Centralizes access to the progress bar and associated messages accross components
(hwcomp,migapp,etc.) and sides (w9x, nt)
Author:
Marc R. Whitten (marcw) 14-Apr-1997
Revision History:
jimschm 19-Jun-1998 Improved to allow revision of estimates, necessary
for NT-side progress bar.
--*/
#include "pch.h"
//
// Types
//
typedef struct {
BOOL Started;
BOOL Completed;
UINT InitialGuess;
UINT TotalTicks;
UINT TicksSoFar;
UINT LastTickDisplayed;
} SLICE, *PSLICE;
typedef struct {
HWND Window;
HANDLE CancelEvent;
PCSTR Message;
DWORD MessageId;
DWORD Delay;
BOOL InUse;
} DELAYTHREADPARAMS, *PDELAYTHREADPARAMS;
#if 0
typedef struct {
HANDLE CancelEvent;
DWORD TickCount;
BOOL InUse;
} TICKTHREADPARAMS, *PTICKTHREADPARAMS;
#endif
//
// Globals
//
BOOL g_ProgBarInitialized = FALSE;
HWND g_ProgressBar;
HWND g_Component;
HWND g_SubComponent;
PBRANGE g_OrgRange;
HANDLE g_ComponentCancelEvent;
HANDLE g_SubComponentCancelEvent;
BOOL *g_CancelFlagPtr;
GROWBUFFER g_SliceArray;
UINT g_SliceCount;
UINT g_MaxTickCount;
UINT g_PaddingTicks;
UINT g_CurrentTickCount;
UINT g_CurrentPos;
UINT g_ReduceFactor;
BOOL g_Reverse = FALSE;
OUR_CRITICAL_SECTION g_ProgBarCriticalSection;
UINT g_CurrentSliceId = (UINT)-1;
//
// Tuning constants
//
#define TICKSCALE 100
//
// Implementaiton
//
VOID
InitializeProgressBar (
IN HWND ProgressBar,
IN HWND Component, OPTIONAL
IN HWND SubComponent, OPTIONAL
IN BOOL *CancelFlagPtr OPTIONAL
)
{
LONG rc;
CHAR Data[256];
DWORD Size;
HKEY Key;
g_ProgressBar = ProgressBar;
g_CancelFlagPtr = CancelFlagPtr;
g_ProgBarInitialized = TRUE;
SendMessage (ProgressBar, PBM_SETPOS, 0, 0);
g_CurrentPos = 0;
SendMessage (ProgressBar, PBM_GETRANGE, 0, (LPARAM) &g_OrgRange);
//
// Create cancel events for delayed messages.
//
g_ComponentCancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
g_SubComponentCancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!g_ComponentCancelEvent || !g_SubComponentCancelEvent) {
DEBUGMSG ((DBG_ERROR, "ProgressBar: Could not create cancel events."));
}
InitializeOurCriticalSection (&g_ProgBarCriticalSection);
g_Component = Component;
g_SubComponent = SubComponent;
DEBUGMSG_IF ((
Component && !IsWindow (Component),
DBG_WHOOPS,
"Progress bar component is not a valid window"
));
DEBUGMSG_IF ((
SubComponent && !IsWindow (SubComponent),
DBG_WHOOPS,
"Progress bar sub component is not a valid window"
));
MYASSERT (!g_SliceCount);
MYASSERT (!g_SliceArray.Buf);
MYASSERT (!g_MaxTickCount);
MYASSERT (!g_PaddingTicks);
MYASSERT (!g_CurrentTickCount);
MYASSERT (g_CurrentSliceId == (UINT)-1);
g_ReduceFactor = 1;
Key = OpenRegKeyStrA ("HKLM\\inapoi");
if (Key) {
Size = 256;
rc = RegQueryValueExA (Key, "", NULL, NULL, (PBYTE) Data, &Size);
CloseRegKey (Key);
if (rc == ERROR_SUCCESS && !lstrcmpiA (Data, "backwards")) {
g_Reverse = TRUE;
}
}
}
VOID
TerminateProgressBar (
VOID
)
{
if (g_ComponentCancelEvent) {
CloseHandle (g_ComponentCancelEvent);
g_ComponentCancelEvent = NULL;
}
if (g_SubComponentCancelEvent) {
CloseHandle (g_SubComponentCancelEvent);
g_SubComponentCancelEvent = NULL;
}
DeleteOurCriticalSection (&g_ProgBarCriticalSection);
FreeGrowBuffer (&g_SliceArray);
g_SliceCount = 0;
g_MaxTickCount = 0;
g_PaddingTicks = 0;
g_CurrentTickCount = 0;
g_CurrentSliceId = -1;
g_Component = NULL;
g_SubComponent = NULL;
g_ReduceFactor = 1;
SendMessage (g_ProgressBar, PBM_SETRANGE32, g_OrgRange.iLow, g_OrgRange.iHigh);
g_ProgBarInitialized = FALSE;
}
UINT
RegisterProgressBarSlice (
IN UINT InitialEstimate
)
{
PSLICE Slice;
UINT SliceId;
MYASSERT (g_ProgBarInitialized);
if (!g_ProgBarInitialized) {
return 0;
}
SliceId = g_SliceCount;
Slice = (PSLICE) GrowBuffer (&g_SliceArray, sizeof (SLICE));
g_SliceCount++;
Slice->Started = FALSE;
Slice->Completed = FALSE;
Slice->TotalTicks = InitialEstimate * TICKSCALE;
Slice->InitialGuess = Slice->TotalTicks;
Slice->TicksSoFar = 0;
Slice->LastTickDisplayed = 0;
return SliceId;
}
VOID
ReviseSliceEstimate (
IN UINT SliceId,
IN UINT RevisedEstimate
)
{
PSLICE Slice;
MYASSERT (g_ProgBarInitialized);
if (!g_ProgBarInitialized) {
return;
}
if (SliceId >= g_SliceCount) {
DEBUGMSG ((DBG_WHOOPS, "ReviseSliceEstimate: Invalid slice ID %u", SliceId));
return;
}
Slice = (PSLICE) g_SliceArray.Buf + SliceId;
if (!g_CurrentTickCount) {
Slice->TotalTicks = RevisedEstimate;
return;
}
if (Slice->Completed) {
DEBUGMSG ((DBG_WHOOPS, "ReviseSliceEstimate: Can't revise completed slice"));
return;
}
if (Slice->InitialGuess == 0) {
return;
}
RevisedEstimate *= TICKSCALE;
MYASSERT (Slice->TicksSoFar * RevisedEstimate > Slice->TicksSoFar);
MYASSERT (Slice->LastTickDisplayed * RevisedEstimate > Slice->LastTickDisplayed);
Slice->TicksSoFar = (UINT) ((LONGLONG) Slice->TicksSoFar * (LONGLONG) RevisedEstimate / (LONGLONG) Slice->TotalTicks);
Slice->LastTickDisplayed = (UINT) ((LONGLONG) Slice->LastTickDisplayed * (LONGLONG) RevisedEstimate / (LONGLONG) Slice->TotalTicks);
Slice->TotalTicks = RevisedEstimate;
}
VOID
BeginSliceProcessing (
IN UINT SliceId
)
{
PSLICE Slice;
UINT u;
UINT TotalTicks;
MYASSERT (g_ProgBarInitialized);
if (!g_ProgBarInitialized) {
return;
}
if (!g_ProgressBar) {
DEBUGMSG ((DBG_WHOOPS, "No progress bar handle"));
return;
}
if (SliceId >= g_SliceCount) {
DEBUGMSG ((DBG_WHOOPS, "BeginSliceProcessing: Invalid slice ID %u", SliceId));
return;
}
if (!g_CurrentTickCount) {
//
// Initialize the progress bar
//
MYASSERT (g_CurrentSliceId == (UINT)-1);
TotalTicks = 0;
Slice = (PSLICE) g_SliceArray.Buf;
for (u = 0 ; u < g_SliceCount ; u++) {
TotalTicks += Slice->InitialGuess;
Slice++;
}
TotalTicks /= TICKSCALE;
g_PaddingTicks = TotalTicks * 5 / 100;
g_MaxTickCount = TotalTicks + 2 * g_PaddingTicks;
g_ReduceFactor = 1;
while (g_MaxTickCount > 0xffff) {
g_ReduceFactor *= 10;
g_MaxTickCount /= 10;
}
SendMessage (g_ProgressBar, PBM_SETRANGE, 0, MAKELPARAM (0, g_MaxTickCount));
SendMessage (g_ProgressBar, PBM_SETSTEP, 1, 0);
if (g_Reverse) {
SendMessage (
g_ProgressBar,
PBM_SETPOS,
g_MaxTickCount - (g_PaddingTicks / g_ReduceFactor),
0
);
} else {
SendMessage (g_ProgressBar, PBM_SETPOS, g_PaddingTicks / g_ReduceFactor, 0);
}
g_CurrentTickCount = g_PaddingTicks;
g_CurrentPos = g_PaddingTicks;
} else if (SliceId <= g_CurrentSliceId) {
DEBUGMSG ((DBG_WHOOPS, "BeginSliceProcessing: Slice ID %u processed already", SliceId));
return;
}
g_CurrentSliceId = SliceId;
Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
Slice->Started = TRUE;
}
VOID
pIncrementBarIfNecessary (
IN OUT PSLICE Slice
)
{
UINT Increment;
UINT Pos;
if (Slice->TicksSoFar >= Slice->TotalTicks) {
Slice->TicksSoFar = Slice->TotalTicks;
Slice->Completed = TRUE;
}
if (Slice->TicksSoFar - Slice->LastTickDisplayed >= TICKSCALE) {
Increment = (Slice->TicksSoFar - Slice->LastTickDisplayed) / TICKSCALE;
Slice->LastTickDisplayed += Increment * TICKSCALE;
if (g_Reverse) {
Pos = ((g_CurrentPos + Slice->TicksSoFar) / TICKSCALE);
Pos += g_PaddingTicks;
Pos /= g_ReduceFactor;
if (Pos > g_MaxTickCount) {
Pos = g_MaxTickCount - (g_PaddingTicks / g_ReduceFactor);
}
SendMessage (g_ProgressBar, PBM_SETPOS, g_MaxTickCount - Pos, 0);
} else {
SendMessage (g_ProgressBar, PBM_DELTAPOS, Increment / g_ReduceFactor, 0);
}
}
}
VOID
pTickProgressBar (
IN UINT Ticks
)
{
PSLICE Slice;
LONGLONG x;
if (!Ticks || g_CurrentSliceId == (UINT)-1 || g_CurrentSliceId >= g_SliceCount) {
return;
}
Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
if (!Slice->InitialGuess) {
return;
}
if (Slice->Completed) {
DEBUGMSG ((DBG_VERBOSE, "Progress slice ID %u already completed", g_CurrentSliceId));
return;
}
MYASSERT (Ticks * TICKSCALE > Ticks);
x = ((LONGLONG) Ticks * TICKSCALE * (LONGLONG) Slice->TotalTicks) / (LONGLONG) Slice->InitialGuess;
MYASSERT (x + (LONGLONG) Slice->TicksSoFar < 0x100000000);
Slice->TicksSoFar += (UINT) x;
pIncrementBarIfNecessary (Slice);
}
BOOL
TickProgressBarDelta (
IN UINT TickCount
)
{
BOOL rSuccess = TRUE;
if (!g_ProgBarInitialized) {
return TRUE;
}
if (g_CancelFlagPtr && *g_CancelFlagPtr) {
SetLastError (ERROR_CANCELLED);
rSuccess = FALSE;
} else {
pTickProgressBar (TickCount);
}
return rSuccess;
}
BOOL
TickProgressBar (
VOID
)
{
if (!g_ProgBarInitialized) {
return TRUE;
}
return TickProgressBarDelta (1);
}
VOID
EndSliceProcessing (
VOID
)
{
PSLICE Slice;
if (!g_ProgBarInitialized) {
return;
}
Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
if (!Slice->InitialGuess) {
Slice->Completed = TRUE;
return;
}
if (!Slice->Completed) {
DEBUGMSG ((DBG_WARNING, "Progress bar slice %u was not completed.", g_CurrentSliceId));
Slice->TicksSoFar = Slice->TotalTicks;
Slice->Completed = TRUE;
pIncrementBarIfNecessary (Slice);
}
g_CurrentPos += Slice->TotalTicks;
if (g_CurrentSliceId == g_SliceCount - 1) {
//
// End of progress bar
//
SendMessage (g_ProgressBar, PBM_SETPOS, g_MaxTickCount, 0);
}
}
BOOL
pCheckProgressBarState (
IN HANDLE CancelEvent
)
{
SetEvent(CancelEvent);
return (!g_CancelFlagPtr || !*g_CancelFlagPtr);
}
BOOL
ProgressBar_SetWindowStringA(
IN HWND Window,
IN HANDLE CancelEvent,
IN PCSTR Message, OPTIONAL
IN DWORD MessageId OPTIONAL
)
{
BOOL rSuccess = TRUE;
PCSTR string = NULL;
EnterOurCriticalSection (&g_ProgBarCriticalSection);
if (g_ProgBarInitialized) {
if (pCheckProgressBarState(CancelEvent)) {
if (Message) {
//
// We have a normal message string.
//
if (!SetWindowTextA(Window, Message)) {
rSuccess = FALSE;
DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
}
}
else if (MessageId) {
//
// We have a message ID. Convert it and set it.
//
string = GetStringResourceA(MessageId);
if (string) {
if (!SetWindowTextA(Window, string)) {
rSuccess = FALSE;
DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
}
FreeStringResourceA(string);
}
ELSE_DEBUGMSG((DBG_ERROR,"ProgressBar: Error with GetStringResource"));
}
else {
//
// Just clear the text.
//
if (!SetWindowTextA(Window, "")) {
rSuccess = FALSE;
DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
}
}
}
else {
//
// We are in a canceled state.
//
rSuccess = FALSE;
SetLastError (ERROR_CANCELLED);
}
}
LeaveOurCriticalSection (&g_ProgBarCriticalSection);
return rSuccess;
}
DWORD
pSetDelayedMessageA (
IN PVOID Param
)
{
DWORD rc = ERROR_SUCCESS;
PDELAYTHREADPARAMS tParams = (PDELAYTHREADPARAMS) Param;
//
// Simply wait for the passed in delay or until someone signals the cancel
// event.
//
switch (WaitForSingleObject(tParams -> CancelEvent, tParams -> Delay)) {
case WAIT_TIMEOUT:
//
// We timed out without cancel being signaled. Set the delayed message.
//
ProgressBar_SetWindowStringA (
tParams -> Window,
tParams -> CancelEvent,
tParams -> Message,
tParams -> MessageId
);
break;
case WAIT_OBJECT_0:
default:
//
// We were canceled (or something strange happened :> Do nothing!
//
break;
}
//
// can set a new thread now
//
tParams->InUse = FALSE;
return rc;
}
VOID
ProgressBar_CancelDelayedMessage (
IN HANDLE CancelEvent
)
{
if (!g_ProgBarInitialized) {
return;
}
SetEvent(CancelEvent);
}
BOOL
ProgressBar_SetDelayedMessageA (
IN HWND Window,
IN HANDLE CancelEvent,
IN LPCSTR Message,
IN DWORD MessageId,
IN DWORD Delay
)
{
BOOL rSuccess = FALSE;
DWORD threadId;
static DELAYTHREADPARAMS tParams;
if (!g_ProgBarInitialized || tParams.InUse) {
return TRUE;
}
if (!pCheckProgressBarState(Window)) {
//
// Fill in the parameters for this call to create thread.
//
tParams.Window = Window;
tParams.CancelEvent = CancelEvent;
tParams.Message = Message;
tParams.MessageId = MessageId;
tParams.Delay = Delay;
//
// Spawn off a thread that will set the message.
//
rSuccess = NULL != CreateThread (
NULL, // No inheritance.
0, // Normal stack size.
pSetDelayedMessageA,
&tParams,
0, // Run immediately.
&threadId
);
if (rSuccess) {
tParams.InUse = TRUE;
}
ELSE_DEBUGMSG((DBG_ERROR,"Error spawning thread in ProgressBar_SetDelayedMessageA."));
}
return rSuccess;
}
#if 0
DWORD
pTickProgressBarThread (
IN PVOID Param
)
{
DWORD rc = ERROR_SUCCESS;
PTICKTHREADPARAMS Params = (PTICKTHREADPARAMS)Param;
BOOL Continue = TRUE;
//
// Simply wait for the passed in delay or until someone signals the cancel
// event.
//
do {
switch (WaitForSingleObject(Params->CancelEvent, Params->TickCount)) {
case WAIT_TIMEOUT:
//
// We timed out without cancel being signaled. Tick the progress bar.
//
if (!TickProgressBarDelta (Params->TickCount)) {
//
// cancelled
//
Continue = FALSE;
}
break;
case WAIT_OBJECT_0:
default:
//
// We were canceled (or something strange happened :> Do nothing!
//
Continue = FALSE;
break;
}
} while (Continue);
//
// can set a new thread now
//
Params->InUse = FALSE;
return rc;
}
BOOL
ProgressBar_CreateTickThread (
IN HANDLE CancelEvent,
IN DWORD TickCount
)
{
BOOL rSuccess = FALSE;
DWORD threadId;
static TICKTHREADPARAMS g_Params;
if (g_ProgBarInitialized && !g_Params.InUse) {
if (pCheckProgressBarState(NULL)) {
//
// Fill in the parameters for this call to create thread.
//
g_Params.CancelEvent = CancelEvent;
g_Params.TickCount = TickCount;
//
// Spawn off a thread that will set the message.
//
if (CreateThread (
NULL, // No inheritance.
0, // Normal stack size.
pTickProgressBarThread,
&g_Params,
0, // Run immediately.
&threadId
)) {
rSuccess = TRUE;
g_Params.InUse = TRUE;
}
ELSE_DEBUGMSG ((DBG_ERROR, "Error spawning thread in ProgressBar_CreateTickThread."));
}
}
return rSuccess;
}
BOOL
ProgressBar_CancelTickThread (
IN HANDLE CancelEvent
)
{
if (!g_ProgBarInitialized) {
return TRUE;
}
return SetEvent(CancelEvent);
}
#endif