Subject Re: Form closing when idle
From Mervyn Bick <invalid@invalid.invalid>
Date Wed, 23 Jun 2021 14:14:36 +0200
Newsgroups dbase.getting-started
Attachment(s) close_on_idle_main.wfmclose_on_idle_sub.wfmclose_on_idle.cfm

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