Gordon McComb
Automation

The Cutting Edge:
How Do I Create Dynamic Dialog Boxes?

In this document:

WordPerfect's macro dialog boxes can pack a mighty fine whollop. Your macro dialogs can include text boxes, radio buttons, check boxes, push buttons, and lots more. Your macro dialogs can collect all sorts of useful info that the user provides; that info is stored in variables, that can be accessed elsewhere in the macro.

The normal macro dialog boxes you create are static. That is, they remain on the screen, and their controls cannot be interactively changed. Values entered by the user are not set into memory until the OK button is clicked.

As an option, WordPerfect for Windows lets you create what's known as dynamic dialog boxes. A dynamic dialog box differs from a static dialog box in one important way: the controls in the dialog can change, even while the dialog is displayed on the screen! Values can also be checked while the dialog box is still visible. You can use this technique to verify an entry before the dialog box is allowed to close.

Dynamic dialog boxes are only slightly more difficult to create that regular static dialog boxes. However, since you have so much more control over how the dialog box behaves, you have to add more code to your macro to provide for all the possibilities and options presented to the user. Macro code for supporting a dynamic dialog box tends to be bigger than the code for supporting a static dialog. As a result, you will want to limit creating dynamic dialogs only when the application really calls for it.

In this column you'll learn how to create a basic dynamic dialog box, and you'll learn several common and popular ways of putting dynamic dialog boxes to work. The following discussion applies to macros created in WPWin 6.x, and later. I'll concentrate on dialog boxes you create using the dialog box commands. You can also use WPWin's dialog box editor to create dynamic dialog boxes. The same basic procedures apply.

Creating a Dynamic Dialog Box

Your dialog needs to things to work: a callback "routine," and a reference to that routine. The callback routine is identified with a Label command. This routine is placed somewhere in the macro, and is called by WPWin whenever the user clicks on a control in the dialog box. It's called a "callback" routine because WPWin "calls back" to the routine whenever something happens in the dialog box. Completing the circuit is a reference to the label used as the callback routine. This reference is placed as the third optional parameter of the DialogDisplay command.

Because dynamic dialog boxes use a callback routine, they are often referred as "callback dialogs."

A Callback Dialog Example

Figure 1 shows a short example of a dialog box that uses a callback. The example displays a dialog box with just an OK button. Clicking the OK button makes the dialog box disappear.

Here's how the macro works. The dialog is defined with the DialogDefine command, in the usual way (if you are unfamiliar with this comment, refer to DialogDisplay in the Macros on-help that comes with WPWin). As shown in the example the DialogDisplay command contains a third parameter, used only with callback dialogs. The third parameter specifies the label you want WPWin to go to whenever a control is selected in the dialog box. The descriptive name Callback is used for the callback label, but this name is completely up to you.

In a static dialog box, the macro is paused at the DialogDisplay command. The macro remains paused until the user clicks a button to "dismiss" the dialog box. At that point, the macro continues. In a callback dialog, the macro does not pause at the DialogDisplay command. Rather, the dialog box is displayed, and the macro continues execution. Because of this, it is necessary to keep the macro "busy" while the dialog box is displayed on the screen. This is the purpose of the LoopCtrl variable, and the Repeat/Until loop.

Note:

In WPWin 7 and later you can dispense with the LoopCtrl variable and the Repeat/Until loop by using the CallbackWait and CallbackResume commands. CallbackWait places the macro in a loop, and CallbackResume releases the loop.

The macro repeats because of the Repeat/Until loop. The loop is ended when the user chooses the OK or Cancel button (or some other button you've specified). When the user chooses OK or Cancel, the macro assigns the Value True to the LoopCtrl variable. This breaks the loop and the macro continues. In the case of the example, the macro end when it hits the Return command, which follows the Repeat/Until loop.

The callback routine itself is identified by the Callback label. This label can be placed anywhere in the macro, but by convention, is normally located close to, but following, the dialog box is serves. The name of the callback routine is up to you, of course. I like the word Callback, because it helps remind me what it's used for.

Notice that the callback routine ends with a Return command. This is very important. Without it, the macro will end prematurely, or else may execute lines of code you don't want to execute when the dialog box is displayed.

Determining the Control that Was Selected

An important part of the callback routine is determining which control in the dialog box was selected. This is done by querying an array that WPWin automatically produces when a callback dialog is used in a macro. The array consists of many elements; most of the time you will be concerned with just element 3 of this array. This element contains the region name, which is the name of the control you specified when you created the control the dialog box. (Some controls, like the standard OK and Cancel buttons, have their own inherent region names, which are assigned by WPWin. These names are OKBttn and CancelBttn, respectively.)

The name of the variable array is the same as the callback label you use. For example, if the callback label is Callback, the name of the array is Callback[]. Conversely, if the callback label is MyDlg, the name of the array is MyDlg[].

Capitalization of region names is very important. You need to match the capitalization exactly or your callback routine will not work. The if test checks to see if the user has clicked on the OK button, which is internally referred to as "OKBttn" (and not Okbttn, okbttn, or any other variation). At a minimum, all dynamic dialog boxes should contain a an instruction such as the if test so that the dialog box can be dismissed, and the macro terminated.

Checking for Multiple Callback Events

A callback event is triggered whenever you click on a control in a dynamic dialog box. Each event makes the macro jump to the callback routine, which is Callback in the previous example. Therefore, for each event you're interested in you can insert an If statement to instruct WPWin what to do when the user selects that particular control. Of course, not every control must include an If statement in the callback routine. Those controls that are not critical to the operation of the dialog box don't need an associated callback event handler.

If statements are well suited for checking each event. However, individual If statements can get quite obtrusive if there are a number of events to check. When you need to monitor three or more callback events, it is almost always better to use a Switch statement than a series of If statements. For example, the macro in Figure 2 checks to see if the user clicks the OK button, the Cancel button, or the Help button:

You will notice something interesting when you run this macro: when you click on the Help button, the "Help goes here" message box appears over the original dialog box. This is an added feature of dynamic dialog boxes: you can display more than one dialog box at a time.

Using Callbacks to Verify Input

One of the primary applications of dynamic dialog boxes is verifying user input before the dialog box is dismissed from the screen. This might come in handy, for example, if you wish to make sure that an edit box contains at least some text (i.e. is not blank). The macro in Figure 3 performs just this task. If the edit box is empty, a message is displayed.

A special Region command is used to retrieve the contents of the edit box. An If test determines if the edit box contains text. If it does not, a message box is displayed. If it does, the LoopCtrl variable is set to True so the dialog box can be dismissed. If you are new to Region command you will want to read up on them in the Macros on-line help. These commands are missing from the help for version 6.0a of WPWin, even though 6.0a supports them. The commands are included in the on-line help provided with WPWin 6.1 and later, as well as the Macros Manual, available from Corel.

The following activities produce a callback event while a dynamic dialog box is displayed:

  • •You choose a a push button, radio button, check box, or hot spot
  • •You click inside a list
  • •You chose close from the system menu
  • •You press Presses Alt+F4
  • •You double-click on the system menu box

Most of the time, you will use a callback to determine which push button was pressed, or which list item was selected.

Using Callbacks in a Real Macro

Journal.wcm, shown in Figure 4, is an example of a macro that relies on callbacks. The callback routine is used to dynamically modify the contents of the dialog box. The macro lets you write a short note for any day of the year. The notes for a given month are stored in a private BIF (binary initialization file), which is kept in your macros directory.

If you want to try the macro, in a new document display the Macro Feature Bar by choosing Tools, Macro, Macro Bar. (If you are using WPWin 6.0a, turn off the smart quote feature, if it is on). Retype it exactly as you see it here. When done, click Save & Compile in the Macro Feature Bar. Name the macro journal, and choose Save. Close the macro when you are done. For your convenience, the journal.wcm macro is provided as a file download (see Figure 4).

To use the macro, play it by choosing Tools, Macro, Play. Type journal, and choose Play. Specify the date of the journal entry you want to create, then click the Show Journal button. Write the journal entry, and choose Set when done. You may write as many journal entries as you like, for any day of any year. When you're done, choose Close until the dialog box is dismissed.

If you want to pack more whollop into your macros, consider adding a callback to it's dialog boxes. The dynamic dialog will help make your macro look more professional, and also easier to use.



Macro Listings

Figure 1 -- callback.wcm

DialogDefine ("Dlg"; 50; 50; 200; 100; 1+16; "Title")
DialogDisplay ("Dlg"; 1; Callback)
LoopCtrl=False
Repeat Until (LoopCtrl)
DialogDestroy ("Dlg")
Return
Label (Callback)
If (Callback[3]="OKBttn")
	LoopCtrl=True
EndIf
Return

Figure 2 -- gethelp.wcm

DialogDefine ("Dlg"; 50; 50; 200; 100; 1+2+16; "Title")
DialogAddPushButton ("Dlg"; "HelpBttn"; 50; 100-19; 32; 13; 0; "&Help")
DialogDisplay ("Dlg"; 0; Callback)
LoopCtrl=False
Repeat Until (LoopCtrl)
DialogDestroy ("Dlg")
Return
Label (Callback)
Switch (Callback[3])
	CaseOf "OKBttn":
		LoopCtrl=True
		OKBttn=True
	CaseOf"CancelBttn":
		LoopCtrl=True
		OKBttn=False
	CaseOf"HelpBttn":
		MessageBox (;; "Help goes here")
EndSwitch
Return

Figure 3 -- yourname.wcm

DialogDefine ("Dlg"; 50; 50; 200; 100; 1+2+16; "Title")
DialogAddText ("Dlg"; 0; 10; 10; 100; 9; 1; "Type your name here")
DialogAddEditBox ("Dlg"; "Edit"; 10; 22; 100; 12; 1; Edit)
DialogDisplay ("Dlg"; 0; Callback)
LoopCtrl=False
Repeat
Until (LoopCtrl)
DialogDestroy ("Dlg")
Return
Label (Callback)
Switch (Callback[3])
	CaseOf "OKBttn":
		RegionGetWindowText (WinText; "Dlg.Edit")
		If (WinText="")
			MessageBox (;; "Please enter some text")
		Else
			LoopCtrl=True
			OKBttn=True
		EndIf
	CaseOf "CancelBttn":
	LoopCtrl=True
	OKBttn=False
EndSwitch
Return

Figure 4 -- journal.wcm

CurDate=?DateMonth + "/" + ?DateDay+"/" +?DateYear
DialogDefine ("Dlg"; 50; 50; 215; 225; 16+512; "WordPerfect Journal")
DialogAddText ("Dlg"; 0; 10; 10; 100; 9; 1; "J&ournal Date")
IfPlatform (Win32!)
	DialogAddDate ("Dlg"; "Date"; 10; 20; 100; 15; 1; Date)
EndIfPlatform
If (?MajorVersion<7)
	DialogAddControl("Dlg"; "Date"; 10; 20; 100; 15; "WPdate20"; 1342373889; ""; Date; 0)
EndIf
DialogAddPushButton ("Dlg"; "PB1"; 120; 20; 80; 13; 0; "Show &Journal")
DialogAddPushButton ("Dlg"; "PB2"; 120; 38; 35; 13; 0; "&Set")
DialogAddPushButton ("Dlg"; "PB3"; 165; 38; 35; 13; 0; "&Delete")
DialogAddText ("Dlg"; "Apt"; 10; 48; 100; 9; 1; "Journal for:")
DialogAddFrame ("Dlg"; 0; 10; 60; 190; 120; 2)
DialogAddEditBox ("Dlg"; "Edit"; 10; 60; 190; 120; 32+64+1024+2048; EDIT; 8000)
DialogAddPushButton ("Dlg"; "CloseBttn"; 160; 190; 40; 13; 1; "&Close")
ApptInactive ()
RegionSetWindowText ("Dlg.Date"; CurDate)
DialogDisplay ("Dlg"; 1; Callback)
LoopCtrl=False
Repeat
Until (LoopCtrl)
DialogDestroy ("Dlg")
Return
Label (Callback)
Switch (Callback[3])
	CaseOf "CloseBttn":
		If (Appt)
			RegionSetFocus ("Dlg.CloseBttn")
			ApptInactive ()
			Callback[3]=""
		Else
			LoopCtrl=True
		EndIf
	CaseOf "PB1":
		Val=""
		RegionGetWindowText (SelDate; "Dlg.Date")
		GenerateBifName ()
		BifRead (STAT; "PopCal"; "Journal"; SelDate; Val; 11; ; ; BIF)
		RegionSetWindowText ("Dlg.Edit"; Val)
		ApptActive ()
	CaseOf "PB2":
		RegionSetFocus ("Dlg.CloseBttn")
		RegionGetWindowText (SelText; "Dlg.Edit")
		GenerateBifName ()
		BifWrite (STAT; "PopCal"; "Journal"; SelDate; SelText; 11; ; BIF)
		ApptInactive ()
	CaseOf "PB3":
		MessageBox (Answer; "Pop Cal"; "Delete journal for ^0?";
			YesNo!+IconQuestion!+HasParameters!; SelDate)
		If (Answer=6)
			GenerateBifName ()
			BifWrite (STAT; "PopCal"; "Journal"; SelDate; ; 11; ; BIF)
			ApptInactive ()
		Else
			RegionSetFocus ("Dlg.Edit")
		EndIf
EndSwitch
Return
Label (ApptActive)
RegionSetWindowText ("Dlg.Apt"; "Journal for: "+ SelDate)
RegionShowWindow ("Dlg.Edit"; 1)
RegionSetFocus ("Dlg.Edit")
RegionEnableWindow ("Dlg.PB1"; 0)
RegionEnableWindow ("Dlg.PB2"; 1)
RegionEnableWindow ("Dlg.PB3"; 1)
RegionEnableWindow ("Dlg.Date"; 0)
Appt=True
Return
Label (ApptInactive)
RegionSetWindowText ("Dlg.Apt"; "Journal for: ")
RegionShowWindow ("Dlg.Edit"; 0)
RegionEnableWindow ("Dlg.PB1"; 1)
RegionEnableWindow ("Dlg.PB2"; 0)
RegionEnableWindow ("Dlg.PB3"; 0)
RegionEnableWindow ("Dlg.Date"; 1)
Appt=False
Return
Label (GenerateBifName)
TextMonth=SubStr (SelDate; 1; StrPos (SelDate; "/")-1)
Switch (TextMonth)
	CaseOf "1": Month="jan"
	CaseOf "2": Month="feb"
	CaseOf "3": Month="mar"
	CaseOf "4": Month="apr"
	CaseOf "5": Month="may"
	CaseOf "6": Month="jun"
	CaseOf "7": Month="jul"
	CaseOf "8": Month="aug"
	CaseOf "9": Month="sep"
	CaseOf "10": Month="oct"
	CaseOf "11": Month="nov"
	CaseOf "12": Month="dec"
EndSwitch
Year=SubStr (SelDate; StrLen (SelDate)-1; 2)
BIF=?PathMacros+"popcal" + Year + "." + Month
Return