Using the Salford Software ClearwinTM library for creating GUIs with FORTRAN

Louis J. Farrugia

Department of Chemistry, Joseph Black Building, University Of Glasgow, Glasgow G12 8QQ, Telephone +44 (0)141 330 5137; FAX +44 (0)141 330 4888;
E-mail:
louis@chem.gla.ac.uk – WWW: http://www.chem.gla.ac.uk/~louis/software/

Although the computer language FORTRAN originally arose in the commercial environment of IBM, the advantages of a freely available and portable high-level language were quickly realised by academic scientists. The introduction of standardised FORTRAN dialects, especially FORTRAN 77, greatly enhanced the acceptability of the language. Indeed, although FORTRAN is one of the earliest high-level languages (with all the disadvantages that entails), it is still in wide use today in the scientific community. This is despite major developments in computing languages, such as object oriented programming. Undoubtedly, one reason for the survival of the "dinosaur" language FORTRAN is that scientific programmers are skilled amateurs in that field, with a disinclination to learn new languages. However, it is also true that FORTRAN is efficient and well suited for many scientific purposes.

The advent of Graphical User Interfaces (GUI) created a problem for FORTRAN (and indeed for most computer languages) in that there is no in-built support for this feature. The implementation of GUI's is often platform specific, though a number of excellent cross-platform libraries have been created, examples of which include Tcl/Tk (Ousterhout), Python (http://www.python.org/), GTK+ (http://www.gtk.org/) and Java (http://java.sun.com/). None of the cross-platform GUI building libraries have gained universal acceptance, possibly because most are interpreted rather than compiled languages, with the speed disadvantages that this entails (though this is becoming much less of an issue as processor speeds increase).

For the FORTRAN programmer, without much experience in C or WindowsTM programming (like this author), the ClearwinTM library created by Salford Software (http://www.salfordsoftware.co.uk/) offers a number of advantages. It consists of a set of "format" functions, which are themselves a binding to the native WindowsTM API (application program interface). It provides a rapid way to adapt existing FORTRAN code to run under WindowsTM , often with little or no change to the code. Of course it also has the serious disadvantage that the code is completely compiler specific. In this short article, I will discuss two examples of using this library to enable easy porting to WindowsTM and to create GUI's for crystallographic FORTRAN programs.

The program Platon (Spek - http://www.cryst.chem.uu.nl/platon/) is designed for Unix systems and is a "multi-purpose crystallographic tool". It includes a wide variety of functionality, and is used extensively in the crystallographic community, for structure visualisation, geometrical calculations, structure validation to name but a few uses. It is distributed as source code by the author and can be easily compiled for a variety of Unix systems running X-Windows.

The program is written almost entirely in standard FORTRAN-77, but there are two problems regarding porting it to WindowsTM . Firstly, it uses a graphical interface, albeit a crude one, which utilises a number of X-Windows calls. These implement functions such as creating/destroying windows with graphical capability, responding to mouse events and simple drawing primitives. Fortunately, the link to these X-Windows calls is solely through the routine xwin in the source file xdrvr.c. This routine is written in C to facilitate linking to the X-Windows libraries and passes or receives information from the main FORTRAN code through its arguments only. In the WindowsTM port, this routine has been replaced by a new FORTRAN routine, which is a fairly literal translation of the C code. For instance the code section ....

if ( *ind == -1) /* check for X11 */

{

theDisplay = XOpenDisplay(display_name);

if(theDisplay != NULL) *z = 1; else *z = 0;

if (*z == 1) XCloseDisplay(theDisplay);

}

else if ( *ind == 0 ) /* close the window */

{

if (wopen == 1) XFlush(theDisplay);

XFreeGC(theDisplay, theGC);

XCloseDisplay(theDisplay);

wopen = 0;

return(0);

}

is replaced by ....

if (ind .eq. -1) then

theDisplay=XopenDisplay()

if(theDisplay .ne. 0) then

z=1

else

z=0

endif

else if (ind .eq. 0) then

if(wopen .eq. 1) call XFlush()

call XCloseDisplay()

wopen = 0

return

The names of the routines and variables in the FORTRAN replacement are kept as closely similar as possible. While this is not strictly necessary, it makes updating this routine much easier, if and when the source code in xdrvr.c changes (which it does only rarely). The calls to X-Windows functions such as XCloseDisplay are replaced with new functions with the same name, which in turn make analogous calls to the ClearwinTM library. For example ....

subroutine XCloseDisplay()

include <windows.ins>

common/platon_control/GraphicsControl

common/platon_l/OK_to_close

logical OK_to_close

integer GraphicsControl

OK_to_close = .true.

GraphicsControl=0

call window_update@(GraphicsControl)

end

The variable GraphicsControl is a handle to a previously created graphics-capable window, and setting its value to zero and then updating it using the ClearwinTM call to window_update@ causes this window to be destroyed.

The second problem with porting Platon to WindowsTM is much more difficult to surmount. This concerns the "System S" facility, which relies on a number of Unix specific features, such as soft links. While the concept of a link also exists in WindowsTM , these links do not behave in the same way as Unix links when using a command line interface. As a result, the "System S" facility has not yet been included in the WindowsTM port of Platon. A preliminary WindowsTM port of Platon using the CYGWIN emulation of GNU Unix (http://www.cygwin.com/) has only had partial success in implementing the "System S" facility. The use of this method is unlikely to be favoured by the general crystallographic community, since it also involves installing a CYGWIN environment and running X-Windows servers. Since Platon is updated very frequently, a script has been written to automate the updates. This script downloads the new source code archive, extracts the FORTRAN file, removes all the "System S" code and makes a few minor editorial changes. The resultant FORTRAN source is then compiled and linked with the routines described above, and a new WindowsTM port copied to the web-site. Unless major changes have been made to the Platon source, this is undertaken completely automatically.

The program WinGX (Farrugia - http://www.chem.gla.ac.uk/~louis/software/wingx/) is a collection of (mainly) public domain crystallographic programs, with a common interface to facilitate transfer of data. Almost all are written in FORTRAN. There are numerous GUI's in this program, all of which have been created with the ClearwinTM libraries, which provide a binding to the WindowsTM API libraries. The entire ClearwinTM functionality is included in one run-time library SALFLIBC.DLL. In WinGX, all the ClearwinTM calls creating GUI's have been encapsulated in another library (WGXLIB04.DLL). This library has calls which in general either create widgets or describe the positioning of widgets in the resultant window. As an example, the full code used to create the following GUI for the LEPAGE option in WinGX will be explained.

[graphical interface for LEPAGE]

Fig. 1: Image of graphical interface for LEPAGE as implemented within WinGX

This GUI is created and destroyed inside the integer function DisplayLepageBox which passes back to the calling routine the values of the acceptance criteria and the required determinant for cell transformation, whether to search for sub-cells or supercells and the chosen lattice type. Default values for the cell constants and lattice type are passed when the function is called. The full code for this routine is as follows...

integer function DisplayLepageBox(latt,par,icnt)

integer icnt(2)

character latt

character*20 lattices(7),clabel(8)

real par(9)

real*8 dpar(8)

lattices(1) = 'Primitive'

lattices(2) = 'I-centred'

lattices(3) = 'R-centred'

lattices(4) = 'F-centred'

lattices(5) = 'A-centred'

lattices(6) = 'B-centred'

lattices(7) = 'C-centred'

clabel(1) = 'a :'

clabel(2) = 'b :'

clabel(3) = 'c :'

clabel(4) = 'alpha :'

clabel(5) = 'beta :'

clabel(6) = 'gamma :'

clabel(7) = '2-fold axis (deg) :'

clabel(8) = 'metrical (Å) :'

par(7) = 1.0

par(8) = 1.0

par(9) = 0.1

do i=1,6

dpar(i) = par(i)

enddo

dpar(7) = par(8)

dpar(8) = par(9)

idet = nint(par(7))

ilatt = 1

do i=1,7

if(latt .eq. lattices(i)(1:1)) ilatt = i

enddo

call wgxOpenDialogBoxW('LEPAGE Control Panel')

call wgxOpenBoxW('named_l','UNIT CELL')

call wgxOpenBoxW('invisible',' ')

call wgxWriteStringW(clabel(1),3,1)

call wgxEditRealW(dpar(1),7)

call wgxWriteStringW(clabel(4),6,1)

call wgxEditRealW(dpar(4),7)

call wgxFormFeedW()

call wgxWriteStringW(clabel(2),3,1)

call wgxEditRealW(dpar(2),7)

call wgxWriteStringW(clabel(5),6,1)

call wgxEditRealW(dpar(5),7)

call wgxFormFeedW()

call wgxWriteStringW(clabel(3),3,1)

call wgxEditRealW(dpar(3),7)

call wgxWriteStringW(clabel(6),6,1)

call wgxEditRealW(dpar(6),7)

call wgxCloseBoxW()

call wgxSpaceW()

call wgxSpaceW()

call wgxOpenBoxW('invisible',' ')

call wgxWriteStringW('Lattice :',7,1)

call wgxListBoxW(8,5,lattices,7,ilatt)

call wgxCloseBoxW()

call wgxCloseBoxW()

call wgxFormFeedW()

call wgxNewLineW()

call wgxOpenBoxW('named_l','ACCEPTANCE CRITERIA')

call wgxWriteStringW(clabel(7),11,1)

call wgxEditRealExW(dpar(7),6,0.0d0,0.0d0,1.0d0)

call wgxFormFeedW()

call wgxWriteStringW(clabel(8),11,1)

call wgxEditRealExW(dpar(8),6,0.0d0,0.0d0,1.0d0)

call wgxCloseBoxW()

call wgxOpenBoxW('named_l','SUPER/SUB-CELLS')

call wgxCheckBoxW('Search for sub-cells',icnt(1))

call wgxFormFeedW()

call wgxCheckBoxW('Search for super-cells',icnt(2))

call wgxFormFeedW()

call wgxTextW('Determinant of cell transformation')

call wgxEditIntegerExW(idet,4,0,1,4)

call wgxGangControlW(2,icnt)

call wgxCloseBoxW()

call wgxCloseDialogBoxW(1,i,0)

if(i .eq. 0) i = 2

DisplayLepageBox = i

if(i .ne. 1) goto 1

do i=1,6

par(i) = dpar(i)

enddo

par(7) = float(idet)

par(8) = dpar(7)

par(9) = dpar(8)

latt = lattices(ilatt)(1:1)

1 end

The first part of this routine initialises some character variables for the display and the remaining unitialised parameters. The first call which creates the GUI is to wgxOpenDialogBoxW(), which sets the style of the dialog box and gives the caption shown in the menu bar. The code for this routine is the one which contains the calls ClearwinTM function winio@ and is compiled in WGXLIB04.DLL.

subroutine wgxOpenDialogBoxW(caption)

implicit none

character*(*) caption

integer winio@,i,l1,leng

logical wgxFontPresent

l1 = min(80,leng(caption))

i=winio@('%sy[3d_thin,thin_border]&')

if(wgxFontPresent('MS Sans Serif')) then

i=winio@('%fn[MS Sans Serif]%ts&',0.85d0)

else

i = winio@('%fd&')

endif

i=winio@('%bg[btnface]%tc[btntext]&')

if(l1 .gt. 0) i=winio@('%ca@&',caption(1:l1))

end

The call wgxOpenDialogBoxW(), is always paired with a call to wgxCloseDialogBoxW(), which is the last call describing the GUI. With the arguments given in this example, this provides the standard two OK/Cancel button widgets which are used in most of the WinGX dialog boxes.

In most cases, the names of the functions called in between are self-explanatory. The paired calls to wgxOpenBoxW() and wgxCloseBoxW() create boxes, which in the above example are either invisible (and used for grouping together widget controls) or are visible as labelled, scored boxes. The contents of the box are defined by the intermediate calls - the first two calls in the first box draw a text string clabel(1) and then provide an edit box where the value of the real parameter dpar(1) (cell constant a) is displayed, and may be modified. The call to wgxFormFeedW() moves the insertion point for the next widget below all previous widgets in that box. A list box widget showing all the acceptable values for the lattice type is produced by wgxListBoxW() with arguments defining the text strings displayed and the finally chosen lattice type - an integer variable ilatt.

The two check box widgets created by separate calls to wgxCheckBoxW() indicate whether the user wishes to search for subcells or supercells. In this example, these two widgets are ganged, using a call to wgxGangControW(), so that only one may be selected at any time, though neither need to be selected. In this way the programmer ensures that only logically sensible commands are returned by the calling function, and in the end passed to the program code of LEPAGE (Spek). The return value of DisplayLepageBox indicates whether the user hit the OK or Cancel button. If the latter were the case, then none of the arguments are updated, and the execution of the remaining LEPAGE code is avoided.

The use of the wgxXXXXW() library calls adds an extra layer between the FORTRAN code and the WindowsTM API libraries, and may seem at first superfluous. However it means that all the code which calls these routine conforms to FORTRAN standards (either 77 or 95). In addition, in the future an entirely different set of GUI creating library functions could be linked to them, allowing the program to be ported to other platforms.

References

L.J. Farrugia, J. Appl. Cryst., 1999, 32, 837-838.

J. Ousterhout, Tcl and the Tk Toolkit, Addison-Wesley, ISBN 0-201-63337-X

A. L. Spek, Acta Crystallogr., Sect A, 1990, 46, C34.