|
|
/*++
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"
#include "migshared.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);
#pragma prefast(suppress:400, "not concerned about different code page")
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
|