Managing Application State

A widget application is usually divided into at least two separate routines, one that creates and realizes the application and another that handles events. These multiple routines need shared access to certain types of information, such as the widget IDs of the application’s widgets and data being used by the application. This shared information is referred to as the application state.

Techniques for Preserving Application State

The following are some techniques you can use to preserve and share application state data between routines.

Using COMMON Blocks

One obvious answer to this problem is to use a COMMON block to hold the state. However, this solution is generally undesirable because it prevents more than a single copy of the application from running at the same time. It is easy to imagine the chaos that would ensue if multiple instances of the same application were using the same common block without some sort of interlocking.

Using a State Structure in a User Value

A better solution to this problem is to use the user value of one of the widgets to store state information for the application.Using this technique, multiple instances of the same widget code can exist simultaneously. Since this user value can be of any type, a structure can be used to store any number of state-related values.

For example, consider the following example widget code:

PRO my_widget_event, event

  WIDGET_CONTROL, event.TOP, GET_UVALUE=state, /NO_COPY

 

  Event-handling code goes here

 

  WIDGET_CONTROL, event.TOP, SET_UVALUE=state, /NO_COPY

END

 

PRO my_widget

  ; Create some widgets

  wBase = WIDGET_BASE(/COLUMN)

  wDraw = WIDGET_DRAW(wBAse, XSIZE=300, YSIZE=300)

 

  ; Realize the base widget and retrieve the widget ID

  ; of the drawable area.

  WIDGET_CONTROL, wBase, /REALIZE

  WIDGET_CONTROL, wDraw, GET_VALUE=idxDraw

 

  ; Create a state structure variable and set the user

  ; value of the top-level base equal to the state variable.

  state = {wDraw:wDraw, idxDraw:idxDraw}

  WIDGET_CONTROL, wBase, SET_UVALUE=state

 

  ; Use XMANAGER to manage the widgets

  XMANAGER, 'my_widget', wBase

END

In this example, we store state information (the widget ID of the draw widget and the index of the drawable area) in a structure variable, and set the user value of the top-level base widget equal to that structure variable. This makes it possible to retrieve the structure using the widget ID contained in the TOP field of any widget event structure that arrives at the event handler routine.

Notice the use of the NO_COPY keyword to WIDGET_CONTROL in the example. This keyword prevents IDL from duplicating the memory used by the user value during the GET_UVALUE and SET_UVALUE operations. This is an important efficiency consideration if the size of the state data is large. (In this example using NO_COPY is not really necessary, as the state data consists only of the two long integers that represent the widget IDs being passed in the state variable.)

While it is important to consider efficiency, using the NO_COPY keyword does have the side effect of causing the user value of the widget to become undefined when it is retrieved using the GET_UVALUE keyword. If the user value is not replaced before the event handler exits, the next execution of the event routine will fail, since the user value will be undefined.

Using a Pointer to the State Structure

A variation on the above technique uses an IDL pointer to contain the state variable. This eliminates the duplication of data and the need for using the NO_COPY keyword.

Consider the following example widget code:

PRO my_widget_event, event

  WIDGET_CONTROL, event.TOP, GET_UVALUE=pState

 

  Event-handling code goes here, accessing the state

  structure via the retrieved pointer.

 

END

 

PRO my_widget_cleanup, wBase

  ; This routine is called when the application quits.

  ; Retrieve the state variable and free the pointer.

  WIDGET_CONTROL, wBase, GET_UVALUE=pState

  PTR_FREE, pState

END

 

PRO my_widget

  ; Create some widgets.

  wBase = WIDGET_BASE(/COLUMN)

  wDraw = WIDGET_DRAW(wBAse, XSIZE=300, YSIZE=300)

 

  ; Realize the base widget and retrieve the widget ID

  ; of the drawable area.

  WIDGET_CONTROL, wBase, /REALIZE

  WIDGET_CONTROL, wDraw, GET_VALUE=idxDraw

 

  ; Create a state structure variable.

  state = {wDraw:wDraw, idxDraw:idxDraw}

 

  ; Place the state structure in a pointer and set the user

  ; value of the top-level base widget equal to the pointer.

  pState = PTR_NEW(state, /NO_COPY)

  WIDGET_CONTROL, wBase, SET_UVALUE=pState, /NO_COPY

 

  ; Call XMANAGER to manage the widgets, specifying the routine

  ; to be called when the application quits.

  XMANAGER, 'my_widget', wBase, CLEANUP='my_widget_cleanup'

END

Notice the following differences between this technique and the technique shown in the previous example:

Each of the above techniques has advantages. Choose a method based on the complexity of your application and your level of comfort with features like IDL pointers and the NO_COPY keyword.