The vApp class serves as the base class for building applications. There must be exactly one instance of an object derived from the vApp class. The base class contains and hides the code for interacting with the host windowing system, and serves to simplify using the windowing system.
You will usually derive a class based on vApp that will serve as the main control center of the application, as well as containing the window objects needed for the user interface. The single instance of the application class is defined in the body of the derived application class code.
The vApp class has several utility methods of general usefulness, as well as several methods that are normally overridden to provide the control interface from the application to the command windows. The derived class will also usually have other methods used to interface with the application.
In order to simplify the control interface between the application and the windows, the vAppWinInfo class has been provided. The application can extend that class to keep track of relevant information for each window. When the NewAppWin method is used to create a window, it will create an appropriate instance of a vAppWinInfo object, and return a pointer to the new object. The base vApp then provides the method getAppWinInfo to retrieve the information associated with a given window.
simSDI This optional parameter is used to specify that Vshould start as a Windows SDI application if it is set to 1. This parameter has no effect for the X version.
fw, fh These are used to specify the size of a menuless and canvasless Vapplication, and are optional.
Your program will not have a C main function. The main reason for this is portability. While you would usually have a main in a Unix based program, MS-Windows does not use main, but rather PASCAL WinMain. By handling how the program gets started and providing the AppMain mechanism, V allows you to ignore the differences. You will still have all the capability to access the command line arguments and do whatever else you would do in main without having to know about getting the host windowing system up and running.
The windowing system will have been initialized before AppMain is called. You can process the command line arguments, and perform other required initializations. The top level command window should also created in AppMain by calling NewAppWin.
Before AppMain is called, the single instance of your derived vApp object must also be constructed, usually by instantiating a static instance with a statement such as static myApp* MyApp = new myApp("ProtoApp"). As part of the construction of the myApp object, the global pointer vApp* theApp is also pointed to the single instance of the vApp or derived myApp object. You can then use theApp anywhere in your code to access methods provided by the vApp class.
Your AppMain should return a 0 if it was successful. A nonzero return value will cause the V system to terminate with an exit code corresponding to the value you returned.
// EVERY V application needs the equivalent of the following line
static myApp myApp("My V App"); // Construct the app.
//==========================>>> AppMain <<<===========================
int AppMain(int argc, char** argv)
{
// Use AppMain to perform special app initialization, and
// to create the main window. This example assumes that
// NewAppWin knows how to create the proper window.
(void) theApp->NewAppWin(0, "My V App", 350, 100, 0);
return 0;
}
The CloseAppWin method is also called when the user clicks the close button of the window. This close button will correspond to the standard close window button depending on the native windowing system. On X Windows, this button will depend on what window manager you are using. On Windows, this corresponds to a double click on the upper left box of the title bar, or the ``X'' box in Windows 95. To abort this "close all" procedure, return 0 from your class.
//======================>>> videApp::CloseAppWin <<<===========================
int videApp::CloseAppWin(vWindow* win)
{
// This will be called BEFORE a window has been really closed.
videCmdWindow* cw = (videCmdWindow*)win; // get our cmd window
if (cw->CheckClose()) // check if OK to close
return vApp::CloseAppWin(win); // if OK, then call vApp method
else
return 0; // otherwise, abort close process
}
If your app needs standard Windows behavior, then you should override CloseLastCmdWindow, and simply return. This will result in an empty MDI framw with a single active File menu with the commands New, Open, and Exit. You should also then override vApp::AppCommand to handle the New and Open cases. It will be harmless to duplicate this code for X apps.
The following code sample, taken from the V Text Editor code, shows how to get standard MDI behavior in a way that is compatible with both Windows and X.
//======================>>> vedApp::AppCommand <<<===========================
void vedApp::AppCommand(vWindow* win, ItemVal id, ItemVal val, CmdType cType)
{
// Commands not processed by the window will be passed here
// switch is used to handle empty MDI frame commands New and Open
// for Windows apps only. Harmless on X.
UserDebug1(Build,"vedApp::AppCmd(ID: %d)\n",id);
switch (id)
{
case M_New:
{
(void*) theApp->NewAppWin(0, "V Text Editor", 100, 50);
return;
}
case M_Open:
{
vedCmdWindow* cw;
cw = (vedCmdWindow*) theApp->NewAppWin(0, "V Text Editor", 100, 50);
cw->WindowCommand((ItemVal)M_Open,(ItemVal)0,(CmdType)0);
return;
}
}
vApp::AppCommand(win, id, val, cType);
}
//===================>>> vedApp::CloseLastCmdWindow <<<======================
void vedApp::CloseLastCmdWindow(vWindow* win, int exitcode)
{
#ifndef V_VersionWindows
vApp::CloseLastCmdWindow(win,exitcode); // call default for X
#endif
}
The default behavior of the base NewAppWin class is to set the window title to name, and the width w and height h. Note that the height and width are of the canvas, and not necessarily the whole app window. If you don't add a canvas to the command window, the results are not specified. Usually, your derived NewAppWin will create an instance of your derived vCmdWindow class, and you will pass its pointer in the win parameter. If the the win parameter is null, then a standard vCmdWindow will be created automatically, although that window won't be particularly useful to anyone.
Your NewAppWin class may also create an instance of your derived vAppWinInfo class. You would pass its pointer to the winInfo parameter. If you pass a null, then the base NewAppWin method also creates an instance of the standard vAppWinInfo class.
The real work done by the base NewAppWin is to register the instance of the window with the internal V run time system. This is why you must call the base NewAppWin method.
NewAppWin returns a pointer to the object just created. Your derived code can return the value returned by the base vApp::NewAppWin, or the pointer it created itself.
The following shows a minimal example of deriving a NewAppWin method.
vWindow* myApp::NewAppWin(vWindow* win, char* name, int w, int h,
vAppWinInfo* winInfo)
{
// Create and register a window. Usually this derived method
// knows about the windows that need to be created, but
// it is also possible to create the window instance outside.
vWindow* thisWin = win;
vAppWinInfo* theWinInfo = winInfo;
if (!thisWin) // Normal case: we will create the new window
thisWin = new myCmdWindow(myname, w, h); // create window
// Now the application would do whatever it needed to create
// a new view -- opening a file, tracking information, etc.
// This information can be kept in the vAppWinInfo object.
if (!theWinInfo) // Create if not supplied
vAppWinInfo* theWinInfo = new myAppWinInfo(name);
// Now carry out the default actions
return vApp::NewAppWin(thisWin, name, w, h, theWinInfo);
}
Note that it is up to you to implement clipboard interaction. The vTextCanvasPane does not provide automatic clipboard support. Thus, your app needs to respond to cut, copy, and paste commands. The clipboard code will send a message to your Command Window to control the sensitivity of the M_Paste command.
How does this translate to V terms? Generally, it is up to you to build your model. Essentially, it will be your data structures and whatever else is needed to implement the core of your app. The controller is usually very closely related to a view of the model. The view and controller will usually be implemented in a vCmdWindow class. You can have different behavior for different views. The power of MVC comes from the ability of a given controller to send a message to all Views of the Model to update themselves as appropriate.
Consider a simple editing program that allows you to edit a data file either in text mode or in hex mode. Your app could have two Views of the Model (your internal representation of the file), one a text view, the other a hex view. Each of these views would be controlled and displayed by individual vCmdWindow classes. If the user makes a change in the text view, then the text view controller would send a message to the hex view to update itself.
V provides two methods to implement MVC, vApp::UpdateAllViews, and vWindow::UpdateView. Your controller sends a message to all other views using UpdateAllViews, and each view receives the message in UpdateView.
Generally, you call UpdateAllViews with sender set to this. UpdateAllViews will not call UpdateView for the sender window because typically the change of the model was a result of an interaction with this window. If you want the sender to be called, call with sender zero.
The hints are passed to UpdateView to help define what action the view needs to take. Generally, hint would have a value set to an enum defined in your derived vApp class. These values would hint about which kind of change is made so that only appropriate actions is taken by the appropriate views. The pHint is typically a pointer to the object representing the model.
V provides two different approaches to handling compute bound applications. The most straight forward approach is to have the computation periodically call the V method vApp::CheckEvents. CheckEvents will process events, and pass the messages to the appropriate V method. This method may be the most appropriate for applications such as simulations. The second technique is to have the V system call a work procedure periodically to allow some computation to be performed. This technique may be most appropriate for applications that have short computations that should be performed even if the user is not entering commands or interacting with the application. The technique is supported by the WorkSlice method.
V uses a standard V vTimer object to implement this behavior. Thus, all of the information about actual time intervals and limits on the number of timers discussed in the vTimer description apply to EnableWorkSlice and WorkSlice.