* Program..: Index.prg * Author...: Jeremy Suiter * Date.....: 26/03/2003 * * Notice...: Copyright (c) 2003, Jeremy Suiter, All Rights Reserved * Notes....: Visual DbEditor progress bar functions (from ASTAG sample) #Include "Font.ch" #Include "Gra.ch" #Include "Xbp.ch" #Include "Appevent.ch" #Include "Common.ch" #Include "Dmlb.ch" #Include "Dbstruct.ch" #Include "Inkey.ch" #Include "DbEditor.ch" // Class for visualizing a progressing process CLASS ProgressBar FROM XbpStatic, Signal PROTECTED: VAR squares, every, _current METHOD displayhoriz, displayvert EXPORTED: VAR oThread VAR maxWait, color VAR minimum, current, maximum ASSIGN METHOD minimum, current, maximum METHOD init , create , destroy , setSize METHOD display, execute, increment ENDCLASS // Initialize the object and set Thread:interval() to zero. This way, // method :execute() is automatically repeated. METHOD ProgressBar:init(oParent, oOwner, aPos, aSize, aPP, lVisible) ::oThread := Thread():New() ::oThread:setInterval(0) ::oThread:atStart:={|| ::xbpStatic:show() } ::Signal:init() ::xbpStatic:init(oParent, oOwner, aPos, aSize, aPP, lVisible) ::xbpStatic:type :=XBPSTATIC_TYPE_RECESSEDBOX ::xbpStatic:paint:={|| ::display() } ::color := GRA_CLR_DARKBLUE ::squares := 1 ::current := 0 ::every := 1 ::maxWait := 100 ::minimum := 0 ::maximum := 100 Return // Request system resources for the progress bar and start the thread. METHOD ProgressBar:create(oParent, oOwner, aPos, aSize, aPP, lVisible) ::xbpStatic:create(oParent, oOwner, aPos, aSize, aPP, lVisible) aSize :=::currentSize() ::squares := Int( aSize[1] / (aSize[2]+1) ) ::oThread:start( {|| ::execute() }) Return // Stop the thread of ProgressBar and release system resources METHOD ProgressBar:destroy // Turn off automatic repetition of :execute(). ::oThread:setInterVal(NIL) If ::oThread:active // Thread is still active. // Signal thread to leave its :wait() state ::signal() EndIf If ThreadObject()<>::oThread // The current thread is not the thread of ProgressBar (self). // Therefore, the current thread must wait for the end of self:thread ::oThread:synchronize(0) EndIf // System resources are released when self:thread has terminated ::xbpStatic:destroy() Return self // Change the size of ProgressBar. Before the size is changed, // everything is overpainted with the background color. METHOD ProgressBar:setSize(aSize) LOCAL oPS, aAttr[ GRA_AA_COUNT ], _aSize oPS :=::lockPS() _aSize :=::currentSize() _aSize[1] -= 2 _aSize[2] -= 2 aAttr [ GRA_AA_COLOR ]:=GRA_CLR_BACKGROUND GraSetAttrArea(oPS, aAttr) GraBox(oPS, {1,1}, _aSize, GRA_FILL) ::unlockPS(oPS) ::xbpStatic:setSize(aSize) Return self // ASSIGN method for :minimum METHOD ProgressBar:minimum(nMinimum) If ::maximum<>NIL .And. nMinimum > ::maximum ::minimum:=::maximum ::maximum:=nMinimum Else ::minimum:=nMinimum EndIf ::current:=::minimum Return self // ASSIGN method for :current METHOD ProgressBar:current(nCurrent) If Valtype(nCurrent) == "N" ::current:=nCurrent If Valtype(::maximum) + Valtype(::minimum) == "NN" ::every :=Int((::maximum-::minimum) / ::squares) IF ::every <=0 ::Every := 1 ENDIF ::_current :=::current ::signal() EndIf EndIf Return ::current // ASSIGN method for :maximum METHOD ProgressBar:maximum(nMaximum) If ::minimum<>NIL .And. nMaximum < ::minimum ::maximum:=::minimum ::minimum:=nMaximum Else ::maximum:=nMaximum EndIf ::current:=::minimum Return self // Increment the current value and refresh display If necessary METHOD ProgressBar:increment(nIncrement) If Valtype(nIncrement)<>"N" nIncrement:=1 EndIf // While a progress is displayed, PROTECTED VAR :_current is incremented // to avoid the overhead of the ASSIGN method :current() ::_current += nIncrement If Int(::_current % ::every) == 0 // This interrupts the ::wait(::maxWait) method in :execute(). // The progress bar is then refreshed immediately in its own thread. // Since the display occurs in a separate thread, it does not // slow down the actual process whose progress is visualized. // Index creation, for example, does not update the display, // but only signals self:thread ::signal() EndIf Return .T. // Refresh progress bar automatically every ::maxWait / 100 seconds // This method runs in self:thread and is automatically restarted // due to :setInterval(0) METHOD ProgressBar:execute ::wait(::maxWait) ::display() Return self // Visualize the current state of a process METHOD ProgressBar:display LOCAL oPS := ::lockPS() LOCAL aSize := ::currentSize() LOCAL aAttr [ GRA_AA_COUNT ] aSize[1] -= 2 aSize[2] -= 2 IF ::current >= ::minimum IF aSize[1] > aSize[2] ::displayHoriz( oPS, aSize, aAttr ) ELSE ::displayVert ( oPS, aSize, aAttr ) ENDIF ENDIF ::unlockPS( oPS ) RETURN self // Display progress bar from left to right (horizontal display) METHOD ProgressBar:displayHoriz(oPS, aSize, aAttr) LOCAL nX, aPos1, aPos2, nCenter // Max. x coordinate for progress bar nX:=aSize[1]*::_current/(::maximum-::minimum) nX:=Min(nX,aSize[1]-1) // Fill the area to the right of the progress bar with background color aAttr[GRA_AA_COLOR]:=GRA_CLR_BACKGROUND GraSetAttrArea(oPS,aAttr) GraBox(oPS, {1+nX,1}, {aSize[1],aSize[2]}, GRA_FILL) // Define fill color for progress bar aAttr[ GRA_AA_COLOR ]:=::color GraSetAttrArea(oPS, aAttr) /* * Calc position for first square */ aPos1 := { 2, 2 } ::squares := Int( aSize[1] / (aSize[2]+1) ) nCenter := 2 + ( aSize[1] - (::squares * (aSize[2]+1)) ) / 2 aPos1[1] := Max( 2, nCenter ) aPos2 := { aPos1[1]+aSize[2]-2 , aSize[2]-1 } /* * paint squares */ DO WHILE aPos2[1] < nX GraBox( oPS, aPos1, aPos2, GRA_FILL ) aPos1[1] += aSize[2]+1 aPos2[1] += aSize[2]+1 ENDDO IF aPos2[1] < aSize[1] GraBox( oPS, aPos1, aPos2, GRA_FILL ) ENDIF Return self /* * Display progress from bottom to top (vertical display) */ METHOD ProgressBar:displayVert( oPS, aSize, aAttr ) LOCAL nY, aPos1, aPos2, nCenter /* * max x coordinate */ nY := aSize[2] * ::_current / ( ::maximum - ::minimum ) nY := Min( nY, aSize[2] ) /* * fill the area on top of the squares with background color */ aAttr [ GRA_AA_COLOR ] := GRA_CLR_BACKGROUND GraSetAttrArea( oPS, aAttr ) GraBox( oPS, {1,nY}, {aSize[1],aSize[2]}, GRA_FILL ) /* * define fill color */ aAttr [ GRA_AA_COLOR ] := ::color GraSetAttrArea( oPS, aAttr ) /* * calculate pos for bottom square */ aPos1 := { 2, 2 } ::squares := Int( aSize[2] / (aSize[1]+1) ) nCenter := 2 + (aSize[2] - (::squares * (aSize[1]+1)) ) / 2 aPos1[2] := Max( 2, nCenter ) aPos2 := { aSize[1]-1, aPos1[2]+aSize[1]-2 } /* * paint squares */ DO WHILE aPos2[2] < nY GraBox( oPS, aPos1, aPos2, GRA_FILL ) aPos1[2] += aSize[1]+1 aPos2[2] += aSize[1]+1 ENDDO IF aPos2[2] < aSize[2] GraBox( oPS, aPos1, aPos2, GRA_FILL ) ENDIF RETURN self