Mr. Cluey
: Classes : CFileDlgAlmost every windows application which manipulates files uses two common dialogs; the Open dialog, and the Save As dialog. You will occassionally wish to test these dialogs, but most of the time they are used in passing - a way to load a document into an application for some later testing. Because these dialogs appear in so many applications, they are excellent candidates for creating special windows classes.
The CFileDlg class is a parent class for both of these dialogs. You could of course code each of the system dialogs independently - but they share so many features in common, it will be much easier to maintain the code if as many of their common features are kept in one place. This is the purpose that the CFileDlg class serves.
First, lets examine the implementation of the CFileDlg class itself:
winclass CFileDlg : DialogBox
{
TextField FileName { msw tag "File name:" ; pm tag "Open filename:"; }
ListBox FileList { msw tag "File name:" ; pm tag "File:"; }
ListBox DirList { msw tag "#2"; pm tag "Directory:";}
PopupList FileType { tag "#1"; pm tag "Type of file:"; msw95 tag "* type:"; }
PopupList Drives { msw tag "Drives:"; pm tag "Drive:" ; }
PushButton OK { tag "OK"; }
PushButton Cancel { tag "Cancel"; }
window wDismiss = Cancel ;
VerifyFilter( list of string lsExpected )
{
list of string lsFileTypes = this.FileType.GetContents () ;
CompareStringLists( lsFileTypes, lsExpected ) ;
}
}
The first thing that should be immediately obvious is the work we are saving by encapsulating the common components of the dialogs; just look at those tags! The concept of a Common dialog appears in most windows environments, but they don't always look all that similar. Tucking as much of the information in here saves later maintenance - I was very happy to have done so when I ported this to OS/2 (which is where the pm tags came from.
The second is the odd verification method. The dialogs are generally created by passing a list of file types to a function - so why not verify that the list is correct?
Notice too the sparsity of the labels in the window declaration. We aren't interested (usually) in testing that the open dialog itself is correct - just that our application uses it correctly. The labels, which our application does not control, are just extra work for us, so get rid of them.
Having assembled the base class, we can now start looking at the modifications that we need to create an open dialog.
winclass OpenDlg : FileDlg
{
tag "Open" ;
PushButton Open { tag OpenButtonTag() ; }
OpenFile( string sFileName )
{
// Set a trap so that the "save changes?" messagebox gets dismissed.
MessageBox.SetTrap( "No" ) ;
Invoke () ;
sleep( 1 ) ;
MessageBox.ClearTrap() ;
FileName.SetText( sFileName ) ;
Open.Click() ;
}
// This is the generic menu item to invoke the Open dialog. If
// the application uses something other than File.Open... then this
// variable needs to be over written.
window wInvoke = WindowParent(this).Menu("File").MenuItem("Open") ;
string OpenButtonTag()
{
string sTag = "OK" ;
// need to create a msw40 gui type....
if GetGuiType() == msw95 || GetGuiType() == mswnt40
{
sTag = "Open" ;
}
return ( sTag ) ;
}
}
First in the declaration of this class is the Tag, which is pretty straight forward. Next comes the definition of the Open button - the tag here would normally be straight forward, but in the past, QAP had trouble recognizing WinNT distinctions, so this odd artifact remains.
OpenFile is the most important piece of the declaration. It is important to understand why the SetTrap() is in place. There is never a circumstance where you will want to use the OpenFile method to accept the opportunity. You might want to test that your application behaves correctly when the Open menu is picked - but you shouldn't use the OpenFile method to test that (instead picking the menu option directly, then checking for the messagebox).
wInvoke tells QAP which menu in your application to use to invoke the dialog. For most applications, File/Open will be correct. Actual instances of the class might overwrite this variable with another (see SysOpen, for an example of this). Note that we use the long notation when specifying the invoke menu - we have no guarantee from here that the parent of the open dialog will have bothered to declare the File and the Open menu items.
The implementation of the Save dialog looks much the same :
winclass SaveAsDlg : FileDlg
{
tag "Save As" ;
PushButton Save { tag SaveButtonTag() ; }
hidecalls SaveFile( string sFileName )
{
Invoke () ;
FileName.SetText( sFileName ) ;
MessageBox.SetTrap("Yes") ;
Save.Click () ;
sleep(1) ;
MessageBox.ClearTrap () ;
}
// This is the generic menu item to invoke the Open dialog. If
// the application uses something other than File.Open... then this
// variable needs to be over written.
window wInvoke = WindowParent(this).Menu("File").MenuItem("Save As") ;
string SaveButtonTag()
{
string sTag = "OK" ;
// This code, before the NT version was fixed, handled
// the problem with the NT using 16 bit dialogs.
// if GetGuiType() == msw95 && !IsWinNT()
if GetGuiType() == msw95
{
sTag = "Save" ;
}
return ( sTag ) ;
}
}
The Save dialog looks much the same; the tag, as we would expect. The Save button on the dialog has a declaration similar to the Open button in the Open dialog - feel free to rip this out and use a simpler one. The default Invoke menu is set up in much the same way as before.
Take a good look at the SetTrap() in the SaveFile method. In the open dialog, we would always want it present. However, you might reasonably want to input the file information into the dialog, then verify that the program does prompt you if the target file already exists. Perhaps a seperate method to handle that circumstance - because again, more often than not you just want to save the file and get on with your life.
Having defined the classes, there remains only implementing the instances for the classes for your application. There are three different ways that you might do this.
The principle advantage of the root window implementation is that declaration of the dialog is independent of the application frame. If for whatever reason you must re-record the frame for the app, the open dialog wont need additional maintenance.
window MainWin MyApp
{
... ;
}
window COpenDlg MyOpen { parent MyApp ; }
window CSaveDlg MySave { parent MyApp ; }
// Syntax for calls
// ----------------
// MyOpen.OpenFile( filename ) ;
// MySave.SaveFile( filename ) ;
The child window implementation is most useful in defining application classes. This ensures that any window derrived from that class automatically has an instance of these dialogs declared.
window MainWin MyApp
{
COpenDlg Open ;
CSaveDlg Save ;
}
// Syntax for calls
// ----------------
// MyApp.Open.OpenFile( filename ) ;
// MyApp.Save.SaveFile( filename ) ;
The advantage of the system dialog implementation is that you don't need to declare new windows each time you create a new application frame. The implementation uses ~ActiveApp, which is a danger - if the wrong application becomes active, your script will quite happily start testing that instead. Oops. For the most part, this isn't a real danger - I use SysOpen and SysSave in most of my testing.
window OpenDlg SysOpen
{
tag "~ActiveApp/Open*" ;
}
window SaveAsDlg SysSaveAs
{
tag "~ActiveApp/Save As" ;
}
// Syntax for calls
// ----------------
// SysOpen.OpenFile( filename ) ;
// SysSave.SaveFile( filename ) ;
| Mr. Cluey : Classes : CFileDlg |