View RSS Feed

Development Team Blog

In Praise of All Things Idle

Rate this Entry
Recent research has suggested that "devil" is actually an old fashioned spelling for "developer". While this does not entirely surprise me, I will leave the theological implication of this to others. What I would like to discuss is how this applies to the old adage "Idle hands are the devil’s workshop". Does this really refer to developers and, if so, how can we make idle hands or, more accurately idle handlers part of our workshop?

In Visual DataFlex revision 12.1, we introduced the cIdleHandler class. We added this so we’d have an easier way to enable and disable our new toolbar menu items. If you were not paying close attention to our "What’s New" section you might have missed this new class and, even if you had read about this class, you might not have paid much attention to it. It’s time to pay attention as this is a really useful class.

What does an Idle Handler object do?

The cIdleHandler is a single purpose class with a simple interface. When enabled, it sends itself the message OnIdle whenever your application is about to go idle. An application is idle, when it is waiting in a loop for user input (mouse or keyboard) and there is nothing to process. Right before your application goes into this waiting mode, the OnIdle event is sent. Because this is fired when your application has nothing to do, this is a good time to tell it to perform various housekeeping tasks. Because this is only fired once, it is efficient.

The idle handler only sends OnIdle events when it is enabled. The idle handler can be enabled and disabled at the appropriate times by setting its pbEnabled property. Normally, you will enable and disable this object when its parent object is activated and deactivated. You can do this in the parent object's Activating and Deactivating events. Here’s an example of using an idle timer in a dbView.
Code:
  Object oIdleView is a dbView
      Set Border_Style to Border_Thick
      Set Size to 200 300
      Set Location to 2 2
   
      Object oIdleHandler is a cIdleHandler
         Procedure OnIdle
             Showln "I’m bored!"
         End_Procedure
      End_Object
      
      Procedure Activating
         Forward Send Activating
         Set pbEnabled of oIdleHandler to True
      End_Procedure
   
      Procedure Deactivating
         Set pbEnabled of oIdleHandler to False
         Forward Send Deactivating 
      End_Procedure
   
  End_Object
If you run this you will see that the application is easily bored. If you want it to stop complaining, you need to treat it like a child. Either keep it busy or pay no attention to it at all.

How would you use an Idle Handler?

Idle handlers are particularly useful for visually enabling and disabling parts of your views. For example you may have an Ok button in a view that should only be enabled when you have all of the information needed to proceed. There are two ways to you could do this. One approach is to find every place in your view where an action may change the Ok button’s status and send a message enabling or disabling that button. Finding all those places can be rather difficult. The other approach is to use an idle handler that will set the button’s status when it has nothing better to do. That’s the approach we want.
Code:
Object oIdleView is a dbView
   
      Set Border_Style to Border_Thick
      Set Size to 200 300
      Set Location to 2 2
   
      Object oButton1 is a Button
          Set Location to 174 237
          Set Label to 'Ok'
      
          // fires when the button is clicked
          Procedure OnClick
              Send Ok    
          End_Procedure
      
          Object oIdleHandler is a cIdleHandler
             Procedure OnIdle
                 Delegate Send OnIdle
             End_Procedure
          End_Object
          
          Procedure Activating
             Forward Send Activating
             Set pbEnabled of oIdleHandler to True
          End_Procedure
      
          Procedure Deactivating
             Set pbEnabled of oIdleHandler to False
             Forward Send Deactivating 
          End_Procedure
          
          Procedure OnIdle
              Boolean bIsValid
              Get IsStuffInMyViewValid to bIsValid
              Set Enabled_State to bIsValid
          End_Procedure
          
          Function IsStuffInMyViewValid Returns Boolean
              Function_Return (Random(2)) // I’ve copyrighted this test
          End_Function
          
      End_Object
   
End_Object
Notice that the oIdleHandler object delegates OnIdle to the parent. This is typical.

While you could add an idle handler to every control in your view that requires dynamic enabling and disabling, you can also create a single handler in your view, which will act as the central controller for this activity. Often this is the desired approach. For example, you could change the order entry view so that it does the following:


  1. Enables the Print button when there is an order to print.
  2. Enables a Save button in the order header section when a header section save is required.
  3. Disables the Order Detail grid when the header needs saving or when there is no header record at all.

Here is the code you’d want to add to your view object to do this:
Code:
  Object oIdle is a cIdleHandler
      Procedure OnIdle
          Delegate Send OnIdle
      End_Procedure
  End_Object
   
  Procedure Activating
      Forward Send Activating
      Set pbEnabled of oIdle to True
  End_Procedure
   
  Procedure Deactivating
      Set pbEnabled of oIdle to False
      Forward Send Deactivating
  End_Procedure
   
  Procedure OnIdle
      Boolean bChanged bRec
      Handle hoServer
      Get Server to hoServer
      Get Should_Save of hoServer to bChanged
      Get HasRecord of hoServer to bRec
      Set Enabled_State of oSaveHdrButton to bChanged
      Set Enabled_State of oOrderDtl_Grid to (not(bChanged) and bRec)
      Set Enabled_State of oPrintBtn to bRec
  End_Procedure
As you can see, this type of approach is easy to code and easy to extend. It also provides a very windows-like solution where the enabling and disabling of controls gives your users the guidance they need. Prior to the introduction of the cIdleHandler implementing this could be pretty difficult. Now it’s so simple you might actually find yourself with idle time on your hands.

Comments

  1. Chris Spencer's Avatar
    John
    Thanks for this gem, I already do us ethe approach you stated and also hook into onNewCurrentRecord to do button enabling etc.
    This looks a much cleaner approach.

    Just one comment , I guess you would not want to overtax the onIdle event with too much processing
  2. Chris Spencer's Avatar
    John
    Did some testing with the Idle handler, One problem I see is that it isn't very useful when a user is enetring data in a form and the data in the form needs to be valid.

    It fires between every keystroke.

    So say a form needs a valid folder for example would you use this method to do the following:

    Code:
    Procedure OnIdle
             string sFolder
             Boolean bOk
             Get Value of oForm to sFolder
             Get IsFolderValid sFolder to bOk
             Set Enabled_State of oSaveHdrButton to bOk
    End_Procedure
    Updated 28-Apr-2010 at 04:14 AM by Chris Spencer
  3. Nils G. Svedmyr's Avatar
    Thank you John.

    Very informative as well as entertaining reading.

    What governs when the machine is idle? Is there some kind of timeout that can be set with a.k.a. a windows api function that can be set to control this?

    - Nils G. Svedmyr