Subject Re: Form closing when idle
From Mustansir Ghor <mustan31@hotmail.com>
Date Wed, 23 Jun 2021 12:22:29 -0400
Newsgroups dbase.getting-started

Dear Mervyn Sir

Thank you.

This will be similar to web app , where concept of timeout has its importance and one needs to login in again to resume the page.

I had reached Chapter 10 on Ken Sir Web tutorial before something came up. I hope to resume it soon.

I understood the amendments. but will put here just to be seen by you, if that this how it should be done.

1. the class definition of forms that are to be opened  from the MainForm will change to

class mustanForm of CLOSE_ON_IDLE from "close_on_idle.cfm"     from
class mustanForm of FORM (the form being mustan.wfm).  Only one statement. Rest of form content will remain same

2. Mustan.wfm form will need its metric to be in pixel.

3. 3 routines will need to be put in all forms that needs to affected in form_onOpen method
      form.close_on_idle = true //change to false to disable close_on_idle
      close_on_idle::form_onOpen()
      form.timer.allow_idle_time := 15  //in seconds

4. If the readmodal forms are to be opened using menu item, then the subform routine (that you had put in the your example in Pushbutton) to be put in its on_click event


Hope this will accomplish my objective of closing the app when it is idle for a given time

Thank you again

Best regards
Mustansir


Mervyn Bick Wrote:

> On 2021/06/21 21:42, Mustansir Ghor wrote:
> > Dear Mervyn Sir
> >
> > The findinstance() works well and did the job.
> >
> > But yet another issue came up. The readmodal form that was opened by menu, does not reset the time of mainform hence making it (mainform) to close even if one is working on the readmodal form.
> >
> > Is there anything that can be done.
>
> You could base the readmodal form on close_on_idle.cfm.  You will need
> to stop the timer on the main form while the readmodal form is open.
> When the readmodal form closes, either by time-out or user action, you
> will need to null the instance of the readmodal form and restart the
> timer for the main form.
>
> In the attached example the time-out value has been set to 20 seconds
> (life is too short to wait the default 2 minutes for testing. :-) ) so
> the count-down is always visible instead of only appearing with a minute
> to go.
>
> Mervyn.
>
> ** END HEADER -- do not remove this line
> //
> // Generated on 2021-06-23
> //
> parameter bModal
> local f
> f = new close_on_idle_mainForm()
> if (bModal)
>    f.mdi = false // ensure not MDI
>    f.readModal()
> else
>    f.open()
> endif
>
> class close_on_idle_mainForm of CLOSE_ON_IDLE from "close_on_idle.cfm"
>    with (this)
>       onOpen = class::FORM_ONOPEN
>       height = 406.0
>       left = 190.0
>       top = 14.0
>       width = 437.0
>       topMost = true
>    endwith
>
>    this.PUSHBUTTON1 = new PUSHBUTTON(this)
>    with (this.PUSHBUTTON1)
>       onClick = class::PUSHBUTTON1_ONCLICK
>       height = 34.0
>       left = 168.0
>       top = 197.0
>       width = 107.0
>       text = "Open readmodal form"
>    endwith
>
>
>    function PUSHBUTTON1_onClick()
>       form.oSub = new close_on_idle_subForm()
>       form.oSub.mdi := false
>       form.timer.enabled = false
>       form.textlabel1.text = ''
>       form.oSub.readmodal()
>       form.oSub = null
>       form.timer.seconds = seconds()
>       form.timer.enabled = true
>       return
>
>    function form_onOpen()
>       close_on_idle::form_onOpen()
>       form.timer.allow_idle_time := 20
>       set procedure to close_on_idle_sub.wfm
>       return
>
> endclass
>
> ** END HEADER -- do not remove this line
> //
> // Generated on 2021-06-23
> //
> parameter bModal
> local f
> f = new close_on_idle_subForm()
> if (bModal)
>    f.mdi = false // ensure not MDI
>    f.readModal()
> else
>    f.open()
> endif
>
> class close_on_idle_subForm of CLOSE_ON_IDLE from "close_on_idle.cfm"
>    with (this)
>       onOpen = class::FORM_ONOPEN
>       height = 406.0
>       left = 674.0
>       top = 21.0
>       width = 437.0
>    endwith
>
>
>    function form_onOpen()
>       close_on_idle::form_onOpen()
>       form.timer.allow_idle_time := 20
>       return
>
> endclass
>
> /* *********************************************
>  
>    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
>