rundialog
OPTIONS

The rundialog statement uses a form as a template to display a modal dialog window. The statement supervises the operation of the dialog until it is closed.


Parameters

This statement has one parameter:

options – This parameter may contain one or more options that alter the operation of the dialog. See the description below for more information.


Description

This statement uses a form as a template to modal display a dialog window. The statement supervises the operation of the dialog until it is closed.

While the modal dialog is open, the program runs in a loop. The rundialog statement uses Panorama’s trigger value (see info(“trigger”), settrigger) to manage the state of the loop. When the trigger becomes Dialog.Close the dialog has been closed, and the loop stops. This very simple example shows the minimum possible modial dialog loop.

loop
    rundialog {Form=Address Height=120 Width=400}
    stoploopif info("trigger")="Dialog.Close"
endloop

With a slight modification, the dialog can appear as a sheet attached to the current window (instead of as a standalone modal dialog). Simply add sheet=true to the list of options.

loop
    rundialog {Form=Address Sheet=TRUE Height=120 Width=400}
    stoploopif info("trigger")="Dialog.Close"
endloop

Note: When used as a sheet, dialogs cannot be nested. Only one dialog can be attached to a sheet at a time (this restriction is imposed by Apple).

The Dialog Form

A form generally needs some special preparation to be used as a modal dialog. At a minimum, you probably want to include Ok and Cancel buttons. These buttons should be set up with a one line procedure:

resume {}

In fact, any button contained in a dialog form should contain this one line procedure. This allows the rundialog statement to handle all of the details for you. The actual code for handling each button will be in the main procedure that contains the rundialog statement, as described in the next section.

Loop States

You can use the info(“trigger”) function to detect different loop states and provide custom code to handle these states. You can also modify the loop state with the settrigger statement. The example below does both. In this example, the dialog is designed to initiate a search when the Ok button is pressed. However, if nothing has been entered into the findThis search box, we don’t want a search to happen.

fileglobal findThis
findThis=""
loop
    rundialog {Form="Find" Height=45 Width=346}
    stoploopif info("trigger")="Dialog.Close"
    if info("trigger") contains "Dialog.OK" // has the ok button been pressed?
        if findThis=""
            nsnotify "You must enter something to search for!"
            settrigger "" // don't close the dialog
	    else
            select exportline() contains findThis // do the search
	    endif
     endif
endloop

The example above customizes the operation of the Ok button (Dialog.OK), you can also customize the operation of the Cancel button (Dialog.Cancel). Note: Later you’ll learn how to rename the Ok and Cancel buttons, but even if they are renamed, you still check for them with Dialog.OK and Dialog.Cancel.

When a button other than the OK or Cancel button is pressed, the info(“trigger”) function will return Button. followed by the title of the button. In this example, the dialog contains an additional button named Clear, pressing this button clears the search text (which we are assuming is in a Text Editor object named Find).

fileglobal findThis
findThis=""
loop
    rundialog {Form="Find" Height=45 Width=346}
    stoploopif info("trigger")="Dialog.Close"
    if info("trigger") contains "Dialog.OK"
        ...
    endif
    if info("trigger") = "Button.Clear"
       objectaction "Find","Open"
       activeobjectaction "setselection",0,32767
       activeobjectaction "clear"
    endif
endloop

Multiple Ok Buttons – Sometimes you may want to have more than one button that terminates the dialog successfully. For example, suppose you have a search dialog with buttons for Find, Select and Cancel. The Find button can be set up to work as the Ok button, but what about the Select button? The code below shows how this is done. There are two things to especially note in this example. On the 5th line, OkButton="Find" tells rundialog to treat the Find button as if it was the Ok button. And on the fourth line from the bottom, settrigger "Dialog.OK" tells rundialog to close the modial dialog window just as if the Ok button had been pressed.

fileglobal findThis
findThis=""
loop
    rundialog
        {Form="Find" Height=45 Width=346 OkButton="Find"}
    stoploopif info("trigger")="Dialog.Close"
    if info("trigger") contains "Dialog.OK" // this is really the Find button
        if findThis=""
            nsnotify "You must enter something to search for!"
            settrigger "" // don't close the dialog
        else
           find exportline() contains findThis
        endif
    endif
    if info("trigger") contains "Button.Select"
        closeactiveobject
        if findThis=""
            nsnotify "You must enter something to select!"
        else
            select exportline() contains findThis
            settrigger "Dialog.OK" // tell the dialog to close
        endif
    endif
endloop

Initializing the Dialog Window – In some cases you may need to perform some initialization after the dialog window has opened. Usually this involves some sort of graphic manipulation — moving an object or changing a font. (Almost any other kind of non-graphic initialization can simply be performed before the loop begins.) We don’t have an example of this, but the basic idea is to check for the trigger value of Dialog.Initialize.

loop
    rundialog {Form="Find" Height=45 Width=346}
    stoploopif info("trigger")="Dialog.Close"
    if info("trigger") contains "Dialog.Initialize"
		/*
		   ... code to initialize procedure goes here ...
		*/
    endif
endloop

Post Dialog Processing

When the modal dialog is finished, the program will continue past the endloop. If additional processing needs to be done, it can be done here. This code can find out how the dialog was closed by examining the dlgResult variable. This is a global variable that will contain one of two possible values – either Ok or Cancel.

This example shows a dialog for setting up a new member in a membership database. If the Ok button is pressed, the new member is added to the database, otherwise, nothing happens.

loop
    rundialog {Form="New Member" Height=120 Width=400}
    stoploopif info("trigger")="Dialog.Close"
endloop
if dlgResult="Ok"
    addrecord
    Name=newMemberName
    Address=newMemberAddress
    City=newMemberCity
    State=newMemberState
    Zip=newMemberZip
endif

If your dialog has multiple buttons that close the dialog (like the Find/Select example earlier), all of these will return a dlgResult value of Ok. If you need to differentiate between different buttons you will need to set up your own separate fileglobal or global variable for doing this.

Dialog Options

The rundialog statement has one parameter — a list of options. The options is a text parameter that uses a syntax similar to an HTML tag to specify one or more options. Each option is specified as an option=value pair, for example form=Entry, title="Enter Quantity", etc. Values aren’t required to be quoted unless they contain spaces or special punctuation characters. (Tip: If you enclose the entire option parameter in pipes, curly brackets or “smart quotes” you will be able to use regular " quotes for the individual options, if necessary.) The following sections describe each of the available dialog options.

Form

This option specifies the name of the form to be used as the template for the modal dialog. This “option” is of course required.

Database

Normally the form is in the current database, but with this option you can use any form in any open database as the template for the modal dialog.

rundialog {form="Weekly Payroll" database="Payroll" ... }

Note: For compatibility with older versions of Panorama, this option can also be specified as file=<name>.

Height and Width

You must specify the initial height and width of your modal dialog. By default the dimensions are specified in points (one point = 1/72 inch). This example specifies a dialog that is 2 inches high and 4 inches wide.

rundialog {form="My Dialog" height=144 width=244}

You can also specify the dimensions in inches (" or in) or centimeters (cm).

rundialog {form="My Dialog" height=2" width=4"}
rundialog {form="My Dialog" height=5cm width=12cm}

If your form is elastic, you can specify the dimensions as a percentage of the total screen width and height. This example creates a dialog that is variable height (80% of the screen height) but fixed width (500 points, or about 7 inches).

rundialog {form="My Dialog" height=80% width=500}

If you specify a negative value for the height or width, the value actually controls the border height or width. This example creates a dialog that is variable height, but with the top of the dialog 1/2 inch from the top of the screen, and the bottom of the dialog 1/2 inch from the bottom of the screen.

rundialog {form="My Dialog" height=-36 width=500}

Note: After determining the height and width, the rundialog statement automatically centers the dialog. If the dialog fits comfortably over the current window it will be centered over that window, otherwise it will be centered on the entire screen.

Sheet

To make the dialog appear as a sheet attached to the current window (instead of as a standalone modal dialog) add sheet=true to the list of options. NOTE: with the introduction of OS 11, Big Sur, Apple has changed the operation of sheets. They no longer slide down from the title bar of the current window. Instead, they open a dialog centered in the current window.

rundialog {form="Options" sheet=true height=200 width=500...}

Note: When used as a sheet, dialogs cannot be nested. Only one dialog can be attached to a sheet at a time (this restriction is imposed by Apple).

Clone

This true/false option specifies whether the form should be opened as a “clone” dialog. Theoretically, this could allow you to open multiple clone dialogs with the same form. More importantly, this allows you to make programmatic changes to the dialog that will be discarded as soon as the dialog is closed. Note: The default for this option is yes (true).

Here is an example that turns off the clone option. We can’t think of any reason why you would do that, but I’m sure someone will come up with one!

rundialog (form="Options" sheet=true clone=no ... }

WindowTitle

The dialog normally displays the name of the form at the top of the dialog window. This option allows you to display any title.

rundialog {form="Avery 5160" height=2" width=3" windowtitle="Mailing Label"}

Note: This option is ignored if the dialog is opened as a sheet attached to the current window instead of as a separate modal dialog window.

Ok and Cancel Buttons

The rundialog statement performs special handling for the Ok and Cancel buttons (see the Loop States section above). If your dialog contains buttons with different names that you would like to operate as Ok and Cancel buttons, use the OkButton= and CancelButton= options to let the rundialog statement know. This example assumes that your form contains buttons named Yes and No that you want to use instead of Ok and Cancel.

rundialog { ... OkButton=Yes CancelButton=No ... }

Note: Even though the buttons have different names, the info(“trigger”) function will still return Dialog.Ok and Dialog.Cancel when the buttons are pressed (as described in the Loop States section above). These values do not change when the button names change.

Cancelling with the Window Close Button

Normally you set up a button in the form for cancelling the dialog. However, you can also enable the window’s close button (the red button in the upper left corner of the window) to perform this function. Use the true/false closewindowbutton= option to do this.

rundialog { ... closewindowbutton=yes ... }

Note: When the user presses the close window button, the modal dialog is immediately closed. info(“trigger”) is never set to Dialog.Cancel, and your code has no chance to prevent the dialog from closing. However, the dlgResult variable is set to Cancel, so any post-dialog code will run fine. If these restrictions are a problem for your dialog, you should not enable the closewindowbutton= option.

Note: This option is ignored if the dialog is opened as a sheet attached to the current window instead of as a separate modal dialog window.

Disabling Window Maximization

Normally the dialog window’s maximization button (the green button in the upper left) is enabled, allowing the user to click to “zoom” the window to maximum size. If you haven’t made the dialog form elastic, you probably want to disable this option, like this:

rundialog { ... maximizablewindow=no ... }

Note: This option is ignored if the dialog is opened as a sheet attached to the current window instead of as a separate modal dialog window.

Automatically Editing Text When Dialog Opens

If your dialog form contains one or more Text Editor objects, you may want the dialog to automatically open one of these objects for editing when the dialog opens. That way the user can just start typing.

To do this, you need to make sure that the Text Editor object has a unique object name (you can set the name in Graphics Mode with the Object Properties palette. Then use the autoedit= option to specify that object will be automatically opened for editing. In this example the object Name will automatically be opened for editing when the dialog opens.

rundialog { ... autoedit="Name" ... }

When the text editing starts – what text is initially selected? That is normally specified in the Object Properties palette for this object, but you can override this using the autoeditstart= and autoeditend= options. This example selects the entire name when the dialog is opened.

rundialog { ... autoedit="Name" autoeditstart=0 autoeditend=-1 ... }

Note that 0 is the spot in front of the first character, and -1 is the spot after the last character.

Automatically Closing after a Delay

Once open, a modal dialog window normally stays open until it is dismissed. Using the timeout= option, however, you can make a dialog automatically close after a specified number of seconds. This example specifies a timeout of 30 seconds (of course the user can also press a button to dismiss the dialog sooner). When the timeout expires, the dialog will close as if the Ok button had been pressed.

rundialog { ... timeout=30 ... }

If the dialog has more than one button, the automatic timeout is normally the same as pressing the Ok button. However, you can alternately specify that the timeout triggers the Cancel button, like this:

rundialog { ... timeout=30 timeoutbutton="cancel" ... }

If the dialog has additional buttons, they cannot be triggered by the timeout. Only Ok or Cancel can be triggered by the automatic timeout.

Note that automatic closing happens the specified number of seconds after the dialog appears – not after a certain amount of inactivity. We don’t recommend using this option with dialogs that have interactive elements (text editors, pop-up menus, checkboxes, etc.). The intended usage is for informative dialogs that don’t need to stay open indefinitely. (Another option for this type of application is to use the nsnotify statement.)

Customized Code for Ok, Cancel and Initialization

In the Loop States section above we showed you how to customize the operation of the dialog by checking info(“trigger”) for Dialog.Ok, Dialog.Cancel and Dialog.Initialize. Instead of hard coding the custom code into the loop as shown in the Loops States section, you can put the custom code into the rundialog options using the dialogok=, dialogcancel= and dialoginitialize= options. Doing this makes the code more confusing and more difficult to debug, but it does have one big advantage – it allows this code to be specified by a calling subroutine and/or to be modified on the fly. If you need this capabilities then you should use these options, otherwise we recommend hard coding the custom code into the dialog loop.

Editing Database Information with a Dialog

Editing database information with a dialog that has OK and Cancel buttons takes some extra effort. You can’t simply edit the data directly because if the user presses the Cancel button you must be able to restore the original data. The solution is to copy the data from the database into variables, edit the variables, and then only copy the data back into the database if the OK button is pressed. You can write code for all this yourself, but it’s easier to let the rundialog statement take care of it all for you.

To illustrate this we’ll assume you have a dialog form set up to edit five database fields —- Name, Address, City, State and Zip. You can’t set up the form to edit these fields directly, because then cancel wouldn’t work. Instead, the form should be set up with five variables that correspond to the fields, but with a prefix (in this case d) -— dName, dAddress, dCity, dState and dZip. Once this is set up you can write code to transfer data back and forth from the field to the variables as you go in and out of the modal dialog.

global dName,dAddress,dCity,dState,dZip
dName=Name
dAddress=Address
dCity=City
dState=State
dZip=Zip
loop
    rundialog {Form=Address Height=120 Width=400}
    stoploopif info("trigger")="Dialog.Close"
    if info("trigger") contains "Dialog.OK"
        Name=dName
        Address=dAddress
        City=dCity
        State=dState
        Zip=dZip
    endif
endloop

This is a lot of extra work – you have to type the field names twice and the variable names three times! Fortunately, the rundialog statement gives you an easier option. By adding Variable: declarations to your dialog option, you can declare the relationship between the fields and variables. Each declaration takes the form

Variable:"<variable>=<field>"

Below is a revised procedure using this technique. A lot shorter, eh? Make sure that these declarations are in the parameter to the rundialog statement, between the { and } characters. (If you set up your form correctly, the Dialog Workshop will write all of the declarations for you, completely automatically!)

loop
    rundialog {Form=Address Height=120 Width=400
        Variable:"dName=Name"
        Variable:"dAddress=Address"
        Variable:"dCity=City"
        Variable:"dState=State"
        Variable:"dZip=Zip"}
    stoploopif info("trigger")="Dialog.Close"
endloop

Note: If the database is a multi-user shared database, extra code with the lockcurrentrecord( function must be added to lock the record before opening the dialog, and to commit the changes to the server afterwards. These modifications are shown in this example.

if lockcurrentrecord()=false()
    alertsheet "Record in use by another user. Please try again later."
    return
endif
loop
    rundialog {Form=Address Height=120 Width=400
        Variable:"dName=Name"
        Variable:"dAddress=Address"
        Variable:"dCity=City"
        Variable:"dState=State"
        Variable:"dZip=Zip"}
    stoploopif info("trigger")="Dialog.Close"
endloop
unlockrecord

Sometimes the data needs to be converted in addition to being copied. Any time a dialog needs to edit a numeric or date field, the declaration needs to include the functions for converting in both directions. Here’s how a numeric Amount field and date StartDate field would be handled.

Variable:"val(«dAmount»)=str(«Amount»)"
Variable:"date(«dStartDate»)=datepattern(«StartDate»,“mm/dd/yy”)"

When conversion functions are used, the variable and field names must always be enclosed in « and » chevrons. The chevrons must be included even if the variable or field name doesn’t contain any blanks or punctuation. If the chevrons are omitted an error will occur when you try to open the dialog.

The rundialog statement normally creates the variables you specify as global variables when the dialog is opened. Using the variabletype= option, you can specify that another type of variable be created instead. The only option that makes any sense here is fileglobal.

variabletype=fileglobal

Here is a revised version of the dialog code that uses fileglobal variables instead of globals. (Remember, if this is a shared multi-user database you’ll also need to add record locking code, as shown above.)

loop
    rundialog {Form=Address Height=120 Width=400
        variabletype=fileglobal
        Variable:"dName=Name"
        Variable:"dAddress=Address"
        Variable:"dCity=City"
        Variable:"dState=State"
        Variable:"dZip=Zip"}
    stoploopif info("trigger")="Dialog.Close"
endloop

We recommend that you avoid using global variables whenever possible.

Custom Dialog Menus

The rundialog statement normally displays only the Apple, Panorama and Edit menus when a dialog is open. This allows you to cut, copy and paste text within the menu. If you want to turn off all the menus, add menu=none to the list of options.

You can also specify your own completely custom menus using Panorama’s Custom Menu feature. Use the menu= option followed by the formula you want to use, like this:

rundialog ||| ... menu={formula} ... |||

In designing the formula, you’ll need to use the same rules as the for the second parameter of the filemenubar and windowmenubar statements. Specifically, you’ll usually want to use the menu(, menuitems(, arraymenuitems( and other similar functions to assemble the custom menu items. The example below adds a custom City menu when the dialog is open. This menu will list all of the cities in the database.

rundialog |||Form="Find/Select" Height=45 Width=346 AutoEdit="Find" OkButton="Find"
       menus={menu("City")+arraymenu(listchoices(City,¶))}
       windowtitle="Locate Information"|||

Note: The rundialog statement always automatically includes the Apple, Panorama and Edit menus, so you don’t need to include those.

To make this custom menu work you’ll need to add code to the dialog loop to check for the menu. This code is identical to the code used for the .CustomMenu procedure. The purple code below shows a typical way to handle the menu.

fileglobal findThis
findThis=""
loop
    rundialog |||Form="Find/Select" Height=45 Width=346
        AutoEdit="Find" OkButton="Find"
        menus={menu("City")+arraymenu(listchoices(City,cr()))}
        windowtitle="Locate Information"|||
    stoploopif info("trigger")="Dialog.Close"
    if info("trigger") beginswith "menu."
        local menuname,menuitem
        splitmenutrigger menuname,menuitem
        if menuname="City"
            objectaction "Find","Open"
            activeobjectaction "inserttext",menuitem
        endif
    endif
    ... rest of dialog event loop ...

Note: In previous versions of Panorama it was necessary to place the line

rundialogmenus

at the top of the .CustomMenu procedure to allow menus to function in a dialog. This is no longer necessary (but is still allowed for compatibility with existing databases).


See Also


History

VersionStatusNotes
10.2UpdatedNew "timeoutbutton" option allows either the Ok or Cancel button to be triggered by the automatic timeout feature.
10.0UpdatedCarried over from Panorama 6.0 with enhancements including the ability to customize the appearance of the dialog window, and the ability to close the dialog using the window's close button.