/* ********************************************* File.......: Close_on_idle.cfm Author ... : Mervyn Bick Date.......: 2021-05-18 This custom form relies on the CALLBACK command and is, therefore, only suitable for dBASE Plus 8 and later versions. This custom form uses a keyboard hook and a mouse hook to monitor instructions to the form. Any keystroke or mouse action resets a timer which is used to close the form after a set period of inactivity. The default is 2 minutes but this can be overridden for individual forms as required. For the final 60 seconds of the allowable idle time a countdown is displayed. The custom form metric is 6 i.e Pixels. Any form derived from this custom form will also have its metric property set to 6. This can be changed in the form designer if required. The keyboard and mouse hooks are completely stable if they are correctly attached when the form opens and detached when the form is closed. Unfortunately there is an extremely annoying side effect when using keyboard and mouse hooks. Any error, even though not associated with the hooks, causes dBASE to close. Provision has, therefore, been made to disable the close_on_idle feature while a form is being developed. Once the form is stable the close_on_idle feature can be activated. ************IMPORTANT******** If this .cfm file is opened in the Custom Form designer the two lines immediately below the class definition will be discarded. It will be necessary to replace them manually. class close_on_idle of FORM custom #include winuser.h #define wm_mousewheel 0x020A //not in winuser.h ******************************** To disable the close_on_idle feature a form_onOpen event handler is required in any form derived from this custom form. function form_onOpen() form.close_on_idle = false //change to true when form is complete //Execute the form_onOpen event handler in the .cfm file //after setting the form's close_on idle property close_on_idle::form_onOpen() //If the default idle time is not appropriate assign a new value //after the .cfm form_onOpen event handler has been executed. form.timer.allow_idle_time := 60*2 //in seconds // Any other code as required return If access is required to the number of seconds before the form closes create function check_time_left(nSec) in a form derived from this custom form. function check_time_left(nSec) //your code return */ ******************************************** class close_on_idle of FORM custom #include winuser.h // See note in comments above #define wm_mousewheel 0x020A //not in winuser.h with (this) onMouseOver = class::FORM_ONMOUSEOVER onMouseOut = class::FORM_ONMOUSEOUT onOpen = class::FORM_ONOPEN onClose = class::FORM_ONCLOSE metric = 6 // Pixels height = 406.0 left = 73.0 top = 5.0 width = 437.0 text = "" endwith this.TEXTLABEL1 = new TEXTLABEL(this) with (this.TEXTLABEL1) height = 22.0 left = 192.0 top = 18.0 width = 228.0 text = "" endwith function attach if this.mhookProc == null CALLBACK CLONG MouseHookWndProc(CINT, CUINT, CUINT) OBJECT this this.mhookProc = GetCallAddress(class::MouseHookWndProc) endif mhInst = GetWindowLong(int(form.hwnd), WH_MOUSE) this.mhook = SetWindowsHookEx(WH_MOUSE, this.mhookProc,mhInst, GetCurrentThreadId()) if this.khookProc == null CALLBACK CLONG KeyHookWndProc(CINT, CUINT, CUINT) OBJECT this this.khookProc = GetCallAddress(class::KeyHookWndProc) endif khInst = GetWindowLong(int(form.hwnd), WH_KEYBOARD) this.khook = SetWindowsHookEx(WH_KEYBOARD, this.khookProc,khInst, GetCurrentThreadId()) return function detach UnhookWindowsHookEx(this.mhook) UnhookWindowsHookEx(this.khook) return function form_onClose() class::detach() this.timer.enabled = false return function form_onMouseOut(flags, col, row) form.mouseOver = false return function form_onMouseOver(flags, col, row) form.mouseOver = true return function form_onOpen() if type('form.close_on_idle') = 'U' form.close_on_idle = true endif if type("GetCurrentThreadId") # "FP" extern CULONG GetCurrentThreadId() kernel32 from "GetCurrentThreadId" endif if type("GetWindowLong") # "FP" extern CLONG GetWindowLong(CHANDLE, CINT) user32 from "GetWindowLongA" endif if type("SetWindowsHookEx") # "FP" extern CLONG SetWindowsHookEx(CINT, CPTR, CHANDLE, CULONG) user32 from "SetWindowsHookExA" endif if type("UnhookWindowsHookEx") # "FP" extern CLOGICAL UnhookWindowsHookEx(CHANDLE) user32 endif if type("CallNextHookEx") # "FP" extern CLONG CallNextHookEx(CHANDLE, CINT, CUINT, CUINT) user32 endif form.mouseOver = true this.timer = new Timer( ) this.timer.parent = this this.timer.seconds = seconds() this.timer.allow_idle_time = 60 *2 // 2 minutes. value in sconds this.timer.onTimer := this.check_action this.timer.interval = 1 this.timer.parent.time_left = 0 this.mhook = null this.khook = null this.mhookProc = null this.khookProc = null if form.close_on_idle = false this.timer.enabled = false else this.timer.enabled = true class::attach() endif return function check_action this.parent.time_left = int(abs(seconds()-this.allow_idle_time-this.seconds)+1) if int(abs(seconds()-this.allow_idle_time-this.seconds)+1) < 61 this.parent.textlabel1.text = 'This form will close in '+int(abs(seconds()-this.allow_idle_time-this.seconds)+1)+' second'+iif(int(abs(seconds()-this.allow_idle_time-this.seconds)+1) = 1,'', 's') else this.parent.textlabel1.text = '' endif if seconds()-this.allow_idle_time-this.seconds >1 this.parent.close() endif if type('this.parent.check_time_left') = 'FP' this.parent.check_time_left(this.parent.time_left) endif return function MouseHookWndProc(hCode, wParam, lParam) if hCode = 0 and form.mouseOver = true if wparam = wm_mousemove or ; wparam = wm_nclbuttondown or ; wparam = wm_lbuttondown or ; wparam = wm_rbuttondown or ; wparam = wm_mbuttondown or ; wparam = wm_mousewheel form.timer.seconds = seconds() endif endif return CallNextHookEx(this.mhook, hCode, wParam, lParam) function KeyHookWndproc(hCode, wParam, lParam) t1 = wParam // virtual key code. t2 = lParam // bit 31 = 0 keydown, bit 31 = 1 keyup if bitset(lparam,31) = false and hCode = 0 and form.mouseOver = true form.timer.seconds = seconds() endif return CallNextHookEx(this.khook, hCode, wParam, lParam) endclass