The second significant conceptual idea I'd like you to notice is the framework's document management feature. To provide Windows standard behavior for data processing applications, the framework provides the concept of "documents" to cover both reports and forms (data output and input). It stores references to these documents in a "metatable", with other information about how you want each document to behave.
The metatable allows your application to have standard "new" and "open" form-picking dialogs, report processing dialog, and for the application generally to make documents available without your having to explicitly make them available on a menu or elsewhere.
The Application Builder provides access to this document-handling table and many of its fields on its Forms and Reports pages. On its Data page, where you add data sources to your project, the Application Builder gives you a chance to generate both forms and reports that match this data. When you choose to do so, more entries are added to the metatable.
You can access the metatable without going through the Application Builder if you wish. As you'll see later in this paper, this gives you access to more advanced use of the fields in this table, as well as the ability to manipulate other fields that you add.
The Application Builder's Forms page is one of three pages in which you adjust the framework-enabled project's metatable.
You may not like the standard framework interface, and the way the way these documents are presented within the interface. You can substitute your own dialogs to access the metatable, or you can have a framework project with no metatable at all.
For small applications, especially, you may want to make documents available directly from menu choices or menu choices, and you may not feel the need to read configuration choices for these documents from a metatable. In another section of this paper, you'll see that it's easy to call the document-executing methods of the application directly yourself, whether from a menu or elsewhere.
The framework has two additional interface features that incorporate document management. Its "quickstart" or "startup" form, and its "favorites" menu are both special ways of making documents accessible without distinguishing between document types. (After all, from a user's point of view, the distinction may be artificial!) These features use metatable entries where available, but do not distinguish between forms and reports as document types. They also allow use of files directly from disk, of any type. Each of these types of document processing is highly configurable, and neither is a required element of your applications.
The APP/EXE is important
The framework relies on the ability of the Project Manager and PJX files to build files into an APP or EXE. Although it can easily use files outside the APP or EXE when you need it to do so, internally it expects to have the organizing power of the APP or EXE to make use of additional components on demand. Don't expect to execute the generated main program and have a framework application run properly; remember that this is an application framework, not a program framework!. (In fact, as you'll see, the generated main program is largely irrelevant to the framework.)
The framework makes good use of the APP or EXE's internal pathing. If you look in the document metatable, you'll notice that file paths are not specified there. You can put them in if you want, for items of your own. The paths may be relative or explicit, whether the files will be on disk at runtime or built into the APP. However, if these documents are Included in the project, these paths should not be needed. You also won't need to put paths in the various application object properties that specify class libraries for any personalized components you create.
There are significant benefits to this approach. The framework strives to uphold the Project Manager's original intention: to allow an APP or EXE be transported to a different path or computer without the need for external files, and to resolve filename references internally. As always, you'll need to add your data, unless you're creating data files as part of your installation routines, and possibly a CONFIG.FPW (the framework gives you a template version), but the framework avoids giving you a lot of external items to think about.
Framework-Delivered Components in Depth
This section will tell you more about the components you get and how to customize them.
As you already know from the numbered steps above, the first set of components is Wizard-generated. The first subsection here will familiarize you with each of these components. The next subsection will tell you how to make the Wizard create different versions of the generated components.
The Wizard generates files with the same name as your project, plus a special suffix, to distinguish them from files you create for your project. You will find the following files in a newly-created framework-enabled project.
_FRAMEWK.VCX subclasses in <PJXNAME>_APP.VCX
These classes are, by default, straight subclasses of the _FRAMEWK class group. They fall into the following main categories:
Each class in your Wizard-generated class library has the prefix "app_", to clearly mark it as a generated class rather than one you have created yourself. For example, the application object is app_application. The Wizard adjusts certain properties of the app_application object, when it creates the application-specific VCX, to reflect the needs of your new application.
The application object instantiates a number of framework-specific classes during normal operation. The class names and libraries of each of these required classes are represented by a couple of properties of the application object. By default, each class-holding property name contains the appropriate "app_ " class name, for example, "app_about" is in the cAboutBoxClass property. The matching class library properties (in this case, cAboutBoxClassLib) are all blank, because the app object defaults to looking in its own class library if this property is left blank.
If you want to use a different class for any application component, you can change the name of the class in the appropriate property and add the appropriate class library name to the matching property.
Although the app_splash class is placed in this library, it's a special case, with neither a matching class nor classlib property. That's because the app class doesn't instantiate the splash; if it did, the splash class would appear too late to be useful!
Instead, the splash class is instantiated by the default/generated main program. For this reason, also, the Application Wizard adjusts properties of your app_splash class in the same way it adjusts properties of the app_application class. In other words, the splash class has direct information about the application's caption, image file, and other "credits" information. That's because there's no way for the splash to get this information from the application object.
Both the _splash and _about classes have some rudimentary visual controls to display this "credits" information. Otherwise they wouldn't do much good out-of-the-box. However, both form classes store the authorship and other "credits" information in appropriate form-level properties. This gives you a chance to transfer the information to any visual elements you like, on startup. In addition, the _splash class takes arguments, which you can use to produce this authorship information dynamically at runtime. You have to write your own instantiating program if you wish to take advantage of these arguments; the generated startup program does not pass the _splash form anything. The section on the generated main program, below, gives you the information you need to do this.
<PJXNAME>_APP.H and the app-referencing global variable
The Application Wizard generates a header file to tune various items used in the non-OOP-files of the generated application. Many of these items are significant only to the wrapper/generated main program, described below, which instantiates the application object. As the contents of this program are not required by the framework, neither are the items in the header file that refer to this program. The generated .H file contains full notes for each item, which you can read at your leisure.
For now, be aware that this file contains one significant #DEFINE , which you may wish to change:
#DEFINE APP_GLOBAL goApp
This #DEFINE holds the single global variable name in the framework system, which allows generated non-OOP components of the framework to reference the application object. If you plan to have numerous "module" type applications, you would want a difference reference variable for each such application in most cases.
Each menu template #INCLUDEs this .H file in its header, precisely to permit menu code to reference the application object and call its methods.
For example, a menu command in one of the menu templates may look like this:
and cleanup code in a menu template may read as follows: IF EMPTY(APP_GLOBAL.cHelpFile) IF NOT APP_GLOBAL.lReleaseUnusedMenuItems SET SKIP OF BAR 1 OF _msystem .T. ELSE RELEASE BAR 1 OF _msystem ENDIF ENDIF
The generated header file is also a convenient place to put any localization strings you may need for application-specific parts of your application. It contains some localization strings for the generated main program and the menus.
The header file also contains the information used by the generated main program to produce a splash screen. Here are the relevant #DEFINEs:
#DEFINE APP_SPLASHCLASS "app_splash" #DEFINE APP_SPLASHCLASSLIB "<PJXNAME>_APP.VCX" * how long should the splash screen stay up if * no key is pressed and if the app object initializes * too quickly? (this figure is in seconds) #DEFINE APP_SPLASHDELAY 3
Main program (NOT!): <PJXNAME>_APP.PRG
This program, by default, will become the main program of your generated application. It is neither required that you use this program nor required that it be the main program. It performs only a few real functions:
As you realize, not every application needs a splash screen (for example a "module type" application might not want one). The generated program gets information about the splash screen from appropriate #DEFINEs in the header file generated for this application, described in the last section.
The generated program provides the app object with the global reference variable information, and a default name for the form mediator object (described later) using these lines:
APP_GLOBAL.cReference =[APP_GLOBAL] APP_GLOBAL.cFormMediatorName = APP_MEDIATOR_NAME
However, this is not a requirement. You can let the application object worry about the reference all by itself, just by assigning a character value to your app subclass's cReference property. The app object takes care of initializing the variable, storing the reference, and cleaning up the reference later. Similarly, you can place a character value in your app subclass's cFormMediatorName reference yourself.
In many cases, you may wish to surround the instantiating program with additional setup code and only issue the .Show() method when you're ready.
If it is not a "normal" type application, the application object will persist past the life of this program, so the generated program passes a reference back to any calling program in this case. Otherwise it will return .T. or .F. to any calling program, depending on whether it was successful in instantiating the application object.
In summary, the generated main program may look important (it is the main program, after all!) and it may look complicated, but it doesn't have to. The following is all you really need to "surround" the creation of the application object in code, minus the error trapping and extraneous splash code stuff:
#INCLUDE [..\<PJXNAME>_APP.H] * the above isn't needed if you pre-load the * character value APP_GLOBAL in the app class * and either hard-code or otherwise make the * APP_CLASSNAME and APP_CLASSLIB items * available to this program. LOCAL loRef loRef = NEWOBJECT(APP_CLASSNAME, APP_CLASSLIB) loRef.cReference = [APP_GLOBAL] && if desired loRef.cFormMediatorName = APP_MEDIATOR_NAME && if desired loRef.Show() && if desired RETURN loRef && if desired
The code above is repeated in your source code, in the APPSUB folder, as MINI.PRG, with additional comments.