The V C++ GUI Framework User Guide:Tutorial:Tutorial code

From EDM2
Jump to: navigation, search

The following is a complete tutorial V application. The source for this tutorial is available int the directory ~/v/tutor.

The Application

tutapp.cpp

//========================================================================
//  tutapp.cpp:     Source file for tutorial V application
//
//      Copyright 1995, Bruce E. Wampler. All rights reserved.
//========================================================================
//
// This file is used to define the main application object. There
// will be exactly one instance of the application object. You will
// usually derive you own app class from the vApp class. This file
// defines a sample tutApp class. The usual purpose of tutApp is to
// start the initial window, and act as a central controller for
// your application. Rather than reading this file sequentially,
// you should skip to the end and read the comments surrounding the
// AppMain function.


//  Files required for tutorial minimal application:
//      tutapp.h:       Header for the min app
//      tutapp.cpp:     Source code for min app
//      tcmdwin.h:      Header code for sample command window
//      tcmdwin.cpp:    Source code for sample command window
//      tdialog.h:      Header for sample modeless dialog
//      tdialog.cpp:    Source for sample modeless dialog
//      tmodal.h:       Header for sample modal dialog
//      tmodal.cpp:     Source for sample modal dialog
//

// First #include header files we need to use.

#include "tutapp.h"     // our header file

//=========================>>> tutApp::NewAppWin <<<======================
  vWindow* tutApp::NewAppWin(vWindow* win, char* name, int w, int h,
    vAppWinInfo* winInfo)
  {
    // This version of NewAppWin is provided with the information
    // required to name and size a window.
    // 
    // Typically, this method would get a file name or other information
    // needed to setup the AppWinInfo class  specifically for the
    // application. Thus, each open window usually represents a view of
    // a file or data object.

    vWindow* thisWin = win;             // local copy to use
    vAppWinInfo* awinfo = winInfo;
    char *myname = name;                // local copy

    if (!*name)
        myname = "Example";            // make up a name
        
    // The UserDebug macros are useful for tracking what is going on.
    // This shows we're building a window.

    UserDebug1(Build,"tutApp::NewAppWin(%s)\n",myname);

    // You may instantiate an instance of the window outside of
    // NewAppWin, or allow NewAppWin to create the instance.

    if (!thisWin)       // Didn't provide a window, so create one.
        thisWin = new tCmdWindow(myname, w, h);

    // The vAppWinInfo class is meant to serve as a database used by the
    // tutApp controller. If you use this feature, you will probably
    // derive your own myAppWinInfo class from vAppWinInfo. The instance
    // of vAppWinInfo created here will be automatically deleted when
    // this window instance is closed through CloseAppWin.

    if (!awinfo)        // Did caller provide an appinfo?
        awinfo = new vAppWinInfo(myname);

    // After you have created an instance of the window and an instance
    // of the AppWinInfo, you MUST call the base vApp::NewAppWin method.
    // You won't need to explicitly keep track of the pointer to
    // each new window -- unless it has to interact with other windows.
    // If that is the case, then you can use your derived vAppWinInfo
    // to coordinate the interaction.

    return vApp::NewAppWin(thisWin,name,w,h,awinfo);
  }

//===========================>>> tutApp::Exit <<<=========================
  void tutApp::Exit(void)
  {
    // This can be called to close all windows. If the app needs to do
    // something special, it can. Otherwise, it can call the general
    // vApp::Exit method, which will perform appropriate calls the the
    // specialized tutApp::CloseAppWin.

    UserDebug(Build,"tutApp::Exit()\n");

    vApp::Exit();       // easy default behavior
  }

//======================>>> tutApp::CloseAppWin <<<=======================
  void tutApp::CloseAppWin(vWindow* win)
  {
    // This will be called BEFORE a window has been unregistered or
    // closed. The app can do whatever it needs to to close down the
    // data associated with this window. (It is invoked explicitly by
    // you in response to a Close menu pick, for example, or when the
    // user clicks the close button. It can also be called by vApp::Exit().
    // After this method cleans up, it can then call the superclass
    // vApp::CloseAppWin to unregister and close this window. Note that
    // the win gives a handle that can be used with vApp::getAppWinInfo
    // to retrieve the AppWinInfo class.

    UserDebug(Build,"tutApp::CloseAppWin()\n");

    // Code to handle close of window (such as saving/closing
    // a file) would go here...

    vApp::CloseAppWin(win);   // Unregister and close the window.
  }

//=====================>>> tutApp::AppCommand <<<=========================
  void tutApp::AppCommand(vWindow* win, ItemVal id, ItemVal val,
    CmdType cType)
  {
    // Any commands not processed by the window WindowCommand
    // method will be passed to here for default treatment.

    UserDebug1(Build,"tutApp::AppCmd(ID: %d)\n",id);
    vApp::AppCommand(win, id, val, cType);
  }

//=======================>>> tutApp::KeyIn <<<============================
  void tutApp::KeyIn(vWindow* win, vKey key, unsigned int shift)
  {
    // Any key strokes not processed by the window will be passed
    // along to here for default treatment.

    vApp::KeyIn(win, key, shift);
  }

//========================================================================
// Remember that any static references to an object are constructed by
// the C++ startup code before main or any other functions are called.
// Thus, the constructor for tutApp (and thus vApp) is invoked before
// anything else happens. This enables V to perform whatever
// initializations are required by the host GUI system - and frees you
// from having to worry about the typical gory details. All this means
// that EVERY V application needs a static instance of the tutApp to
// get things rolling. Note that the global variable theApp is set to
// point to this instance, and is the easiest way to access the vApp
// and tutApp methods (e.g., theApp->Exit()).
//========================================================================

  static tutApp tut_App("TutorApp");  // The single instance of the app

//===========================>>> AppMain <<<==============================
  int AppMain(int argc, char** argv)
  {
    // The V framework defines the instance of main. After some
    // processing of command line arguments, AppMain is called with
    // cleaned up command line arguments. Note that at this time, no
    // windows have been defined. Normally, AppMain is the place to
    // start up the first window. You can perform any initialization you
    // need to do here.

    (void) theApp->NewAppWin(0, "Tutorial V Example", 350, 100, 0);

    // At this point, the window is up, and all events are being
    // routed through its methods.

    // We MUST return 0 if the status is OK at this point.
    return 0;
  }

tutapp.h

//========================================================================
//      tutapp.h:  Header file for tutorial V application
//
//      Copyright 1995, Bruce E. Wampler. All rights reserved.
//========================================================================

#ifndef TUTAPP_H                // Standard technique for avoiding
#define TUTAPP_H                // problems with multiple #includes

#ifdef vDEBUG
#include <v/vdebug.h>
#endif

#include <v/vapp.h>     // We are derived from vApp
#include <v/vawinfo.h>  // Need for app info

#include "tcmdwin.h"            // we use our tCmdWindow class

    class tutApp : public vApp
      {
        friend int AppMain(int, char**); // allow AppMain access

      public:           //---------------------------------------- public

        tutApp(char* name) : vApp(name) {}      // just call vApp
        virtual ~tutApp() {}

        // Routines from vApp that are normally overridden

        virtual vWindow* NewAppWin(vWindow* win, char* name, int w, int h,
           vAppWinInfo* winInfo);
        virtual void Exit(void);
        virtual void CloseAppWin(vWindow* win);
        virtual void AppCommand(vWindow* win, ItemVal id,
                ItemVal val, CmdType cType);
        virtual void KeyIn(vWindow* win, vKey key,
            unsigned int shift);

        // New routines for this particular app go here

      protected:        //------------------------------------- protected

      private:          //--------------------------------------- private

      };
#endif

The Command Window

tcmdwin.cpp

//========================================================================
//  tcmdwin.cpp:     Source file for tutorial cmdwin class
//
//      Copyright 1995, Bruce E. Wampler. All rights reserved.
//========================================================================

// This file contains the source code for a typical command window
// derived from the vCmdWindow class. It will contain the definitions
// of the menu bar and command and status bars. It represents the main
// interaction point with the user.
// 
// We start out with the #includes needed to define this class plus
// any V utility dialogs such as vNotice we use.

#include <v/vnotice.h>  // so we can use notice
#include <v/vkeys.h>    // to map keys
#include <v/vutil.h>    // for utilities
#include <v/vfilesel.h> // for file select

#include "tcmdwin.h"            // our header file

// Now, we define static arrays for the menus, command bars, and
// status bars used by this window. This consists of defining the
// constants needed for IDs, followed by the static declarations of
// the menu and command arrays. Note that V predefines quite a few
// standard IDs which you can use instead of defining your own.

  // Start ID defines for the main window at 100

  const ItemVal m_CheckMe = 100;  // for CheckMe command
  const ItemVal m_CopySens = 101; // for Set Copy Sensitive
  const ItemVal m_Dialog = 102;   // to pop up the dialog
  const ItemVal m_ModalDialog = 103; // for modal dialog
  const ItemVal m_Clear = 104;    // Clear screen

// Now, the static declarations of the menu arrays. You first define
// the pulldown menus, one for each main menu bar label.

    static vMenu FileMenu[] =    // Items for File menu
      {
        {"New",M_New,isSens,notChk,noKeyLbl,noKey,noSub},
        {"Open",M_Open,isSens,notChk,noKeyLbl,noKey,noSub},
        {"Save",M_Save,notSens,notChk,noKeyLbl,noKey,noSub},
        {"Save As",M_SaveAs,notSens,notChk,noKeyLbl,noKey,noSub},
#ifdef vDEBUG                   // Defines V debug code
        {"-",M_Line,notSens,notChk,noKeyLbl,noKey,noSub},
        {"Debug",M_SetDebug,isSens,notChk,noKeyLbl,noKey,noSub},
#endif
        {"-",M_Line,notSens,notChk,noKeyLbl,noKey,noSub},
        {"Exit",M_Exit,isSens,notChk,noKeyLbl,noKey,noSub},
        {NULL}
      };

    static vMenu EditMenu[] =    // Items for Edit menu
      {
        {"Cut",M_Cut,notSens,notChk,noKeyLbl,noKey,noSub},
        {"Copy",M_Copy,notSens,notChk,noKeyLbl,noKey,noSub},
        {"Paste",M_Paste,notSens,notChk,noKeyLbl,noKey,noSub},
        {NULL}
      };

    static vMenu TestMenu[] =   // Items for Test menu
      {
        {"CheckMe",m_CheckMe,isSens,notChk,noKeyLbl,
            noKey,noSub},
        {"Copy Sensitive",m_CopySens,isSens,notChk,noKeyLbl,
            noKey,noSub},
        {"Dialog",m_Dialog,isSens,notChk,noKeyLbl,
            noKey,noSub},
        {"Modal Dialog",m_ModalDialog,isSens,notChk,noKeyLbl,
            noKey,noSub},
        {NULL}
      };

    // Now, define the items on the menu bar

    vMenu StandardMenu[] =     // The menu bar with three items
      {
        {"File",M_File,isSens,notUsed,notUsed,noKey,&FileMenu[0]},
        {"Edit",M_Edit,isSens,notUsed,notUsed,noKey,&EditMenu[0]},
        {"Test",M_Test,isSens,notUsed,notUsed,noKey,&TestMenu[0]},
        {NULL}
      };

// We now define a command bar. Command bars are optional, and there
// may be more than one. You can place any CommandObject you want on a
// command bar.

    static CommandObject CommandBar[] =  // A simple command bar
      {
        {C_Label,999,0 ,"Command Bar",NoList,CA_None,
            isSens,NoFrame,0,0},
        {C_Button,M_Copy,M_Copy,"Copy",NoList,CA_None,
            notSens,NoFrame,0,0},
        {C_Button,m_Dialog,m_Dialog,"Dialog",NoList,CA_None,
            isSens,NoFrame,0,0},
        {C_Button,m_Clear,m_Clear,"Clear",NoList,CA_None,
            isSens,NoFrame,0,0},
        {C_Button,M_Exit,M_Exit,"Exit",NoList,CA_None,
            isSens,NoFrame,0,0},
        {C_EndOfList,0,0,0,0,CA_None,0,0,0}  // This ends list
      };

// Sometimes it is easier to define IDs near the definition of
// the dialog or status bar using them.

  const ItemVal m_cmdMsg = 110;
  const ItemVal m_cmdCount = 111;
  const ItemVal m_keyMsg = 112;
  const ItemVal m_keyVal = 113;

    static vStatus StatBar[] =    // Define a simple status bar
      {
        {"Commands issued: ",m_cmdMsg,CA_NoBorder,isSens,0},
        {" ",m_cmdCount,CA_None,isSens,0},
        {"Last keypress: ",m_keyMsg,CA_NoBorder,isSens,0},
        {"   ",m_keyVal,CA_None,isSens,0},
        {0,0,0,0,0}           // This ends list
      };

    static int copy_sens = 0;   // local for tracking sensitive

//======================>>> tCmdWindow::tCmdWindow <<<====================
  tCmdWindow::tCmdWindow(char* name, int width, int height) :
    vCmdWindow(name, width)
  {
    // This is the constructor for tCmdWindow. 
    
    UserDebug1(Constructor,"tCmdWindow::tCmdWindow(%s) Constructor\n",name)

    // The "Standard" window will consist of a menubar, a canvas, an
    // optional button bar, and an optional status bar.
    // 
    // First, create and add the proper panes to the CmdWindow
    // Note: there must be a corresponding delete in the destructor

    // Create and add the standard Menu Bar to this window
    myMenu = new vMenuPane(StandardMenu);
    AddPane(myMenu);

    // Create and add our Canvas pane to this window
    myCanvas = new tCanvasPane;
    AddPane(myCanvas);

    // Create and add the command pane to this window
    myCmdPane = new vCommandPane(CommandBar);
    AddPane(myCmdPane);

    // Create and add the Status Bar to this window
    myStatus = new vStatusPane(StatBar);
    AddPane(myStatus);

    // In the V model, a window may have dialogs. Each dialog used
    // by a window must have an instance pointer. The easiest way
    // to create dialogs is to construct each one using a new here
    // which only defines the dialog - you need to use its
    // ShowDialog method at the appropriate time to display it).
    // You delete dialogs in the destructor for this window.
    // 
    // Now, create whatever dialogs this app defines:
    // instances of tDialog and tModalDialog

    sampleDialog = new tDialog(this);
    sampleModalDialog = new tModalDialog(this);

    // FINALLY, after all the panes have been constructed and
    // added, we must show the window!

    ShowWindow();
  }

//=====================>>> tCmdWindow::~tCmdWindow <<<====================
  tCmdWindow::~tCmdWindow()
  {
    UserDebug(Destructor,"tCmdWindow::~tCmdWindow() destructor\n")

    // Now put a delete for each new in the constructor.

    delete myMenu;
    delete myCanvas;
    delete myStatus;
    delete myCmdPane;
    delete sampleDialog;
    delete sampleModalDialog;
  }

//========================>>> tCmdWindow::KeyIn <<<=======================
  void tCmdWindow::KeyIn(vKey keysym, unsigned int shift)
  {
    // Keystrokes are routed to this window. This example code shows very
    // simple processing of keystrokes, and how to update the m_keyVal
    // field of the status bar.

    static char ctrl[] = "^X ";
    static char chr[] = " X ";

    if (VK_IsModifier(keysym))
        SetString(m_keyVal, "mod");     // change status bar
    else if (keysym < ' ')              // ctrl char
      {
        ctrl[1] = keysym + '@';
        SetString(m_keyVal, ctrl);      // change status bar
      }
    else if (keysym < 128)              // normal printable char
      {
        chr[1] = keysym;
        SetString(m_keyVal, chr);       // change status bar
      }
    else 
        SetString(m_keyVal, "+++");     // change status bar
  }

//====================>>> tCmdWindow::WindowCommand <<<===================
  void tCmdWindow::WindowCommand(ItemVal id, ItemVal val, CmdType cType)
  {
    // All commands generated from this window's menus and dialog bars
    // are routed through here.  The easiest way to handle commands is to
    // use a single, sometimes large switch. Each time you add a command
    // to a menu or command bar, add a case to the switch here. In this
    // example, we use the V Notice dialog to display entered commands.

    static int cmdCount = 0;    // Used for sample status update
    vNoticeDialog note(this);   // Used for default actions
    char buff[20];              // buffer for status bar

    ++cmdCount;                 // count commands that have been issued
    IntToStr(cmdCount,buff);    // Use V utility routine to get string
    SetString(m_cmdCount, buff);        // change status bar

    UserDebug1(CmdEvents,"tCmdWindow:WindowCommand(%d)\n",id)

    switch (id)                 // The main switch to handle commands
      {
        // File Menu commands ------------------------------------

        case M_New:             // For this example, we will open a
          {                     // new window using our NewAppWin.
            (void) theApp->NewAppWin(0,"",250,100);
            break;
          }

        case M_Open:            // This demos vFileSelect dialog
          {
            char name[100] = "";        // start out with null name
            vFileSelect fsel(this);     // an instance of vFileSelect
            int fI;                     // Filter index
            static char* filter[] = {   // Filter for file select
                "*", "*.txt", "*.c *.cpp *.h", 0 };

            // Show the file select dialog
            int ans = fsel.FileSelect("Open file",name,99,filter,fI);

            if (ans && *name)   // User picked a file name
              {
                SetTitle(name); // Set title of window to name
                note.Notice(name);  // Show the name
              }
            else                // Notify no name selected
                note.Notice("No file name selected.");
          }

        case M_Save:            // This would usually save a file
          {
            note.Notice("Save");
            break;
          }

        case M_SaveAs:          // Save to a specified name
          {
            note.Notice("Save As");
            break;
          }

#ifdef vDEBUG                   // Include debugging like this
        case M_SetDebug:
          {
            vDebugDialog debug(this);   // an instance of debug 
            debug.SetDebug();           // dialog - let user set
            break;
          }
#endif

        case M_Exit:            // Standard exit command
          {                     // Invoke the standard app Exit
            theApp->Exit();     // to close all windows
            break;              // will never get here
          }

        // Edit Menu commands ------------------------------------
        case M_Cut:             // Standard items for Edit menu
          {
            note.Notice("Cut");
            break;
          }

        case M_Copy:
          {
            note.Notice("Copy");
            break;
          }

        case M_Paste:
          {
            note.Notice("Paste");
            break;
          }

        // Test Menu commands ------------------------------------
        case m_CheckMe:         // Demonstrate using a checked menu
          {
            ItemVal curval = GetValue(id); // Get current status
            SetValue(m_CheckMe,!curval,Checked); // Toggle check

            if (curval)                 // Change menu label
                SetString(m_CheckMe,"Check Me");
            else
                SetString(m_CheckMe,"UnChk Me");
            break;
          }

        case m_CopySens:        // Demo changing sensitivity
          {
            copy_sens = !copy_sens;     // toggle
            // This will change both menu and command button
            SetValue(M_Copy,copy_sens,Sensitive);
            break;
          }

        case m_Dialog:          // Invoke our dialog
          {
            if (!sampleDialog->IsDisplayed())   // not twice!
                sampleDialog->ShowDialog("Sample Modeless Dialog");
            break;
          }

        case m_ModalDialog:     // Invoke our modal dialog
          {
            ItemVal val, id;
            id = sampleModalDialog->ShowModalDialog("Sample Modal",val);
            // Now do something useful with id and val ...
            break;
          }

        case m_Clear:           // Clear the canvas
          {
            myCanvas->Clear();  // Invoke the canvas Clear
            break;
          }

        default:                // route unhandled commands up
          {                     // to superclass
            vCmdWindow::WindowCommand(id, val, cType);
            break;
          }
      }
  }

tcmdwin.h

//========================================================================
//      tcmdwin.h:  Header file for tutorial V command window
//
//      Copyright 1995, Bruce E. Wampler. All rights reserved.
//========================================================================
//
// Derive a window from the vCmdWindow class

#ifndef TCMDWIN_H
#define TCMDWIN_H

#include <v/vcmdwin.h>  // So we can use vCmdWindow
#include <v/vmenu.h>    // For the menu pane
#include <v/vstatusp.h> // For the status pane
#include <v/vcmdpane.h> // command pane

#ifdef vDEBUG
#include <v/vdebug.h>
#endif

#include "tdialog.h"    // user defined: tDialog
#include "tmodal.h"     // user defined: tModalDialog
#include "tcanvas.h"    // user defined: tCanvasPane

    class tCmdWindow : public vCmdWindow
      {
        friend int AppMain(int, char**);        // allow AppMain access

      public:           //---------------------------------------- public
        tCmdWindow(char*, int, int);    // Constructor with size
        virtual ~tCmdWindow();          // Destructor
        virtual void WindowCommand(ItemVal id,ItemVal val,CmdType cType);
        virtual void KeyIn(vKey keysym, unsigned int shift);

      protected:        //------------------------------------- protected

      private:          //--------------------------------------- private

        // Each user CmdWindow should conform to a "Standard" window,
        // which includes a menu bar, a canvas, an optional command bar,
        // and an optional status bar.

        vMenuPane* myMenu;              // For the menu bar
        tCanvasPane* myCanvas;          // For the canvas
        vStatusPane* myStatus;          // For the status bar
        vCommandPane* myCmdPane;        // for the command pane

        // Each user CmdWindow will probably have some dialogs and
        // subwindows. Declare pointers to each instance here.

        tDialog* sampleDialog;
        tModalDialog* sampleModalDialog;
      };
#endif

The Canvas

tcanvas.cpp

//========================================================================
//      tcanvas.cpp - source for tutorial canvas
//
//      Copyright 1995, Bruce E. Wampler, All Rights Reserved.
//========================================================================
//
// Each V application usually needs a canvas. In order to handle
// various events: mouse, redraw, resize, and scroll, you will need to
// derive your own canvas class. The base V vCanvasPane class can only
// draw -- it does not have any memory of what has been drawn on the
// screen (the vTextCanvasPane does handle redrawing, but is still
// limited). Thus, your class will usually be responsible for handling
// redrawing. This example is very simple. It lets the user draw
// lines - up to 200 - and will redraw the screen when it has been
// exposed.

// The example does not handle scrolling.

#include "tcanvas.h"            // include our header file

//====================>>> tCanvasPane::tCanvasPane <<<====================
  tCanvasPane::tCanvasPane()
  {
    // The constructor initializes our simple data structure.

    _mouseDown = 0; _nextpt = 0;
    _begx = -1; _begy = -1; _curx = -1; _cury = -1;
    _pt = new point[200];       // allocate only 200 lines
  }

//-===================>>> tCanvasPane::tCanvasPane <<<====================
  tCanvasPane::~tCanvasPane()
  {
    delete [] _pt;              // free the point array
  }

//======================>>> tCanvasPane::Clear <<<========================
  void tCanvasPane::Clear()
  {
    vCanvasPane::Clear();       // clear the canvas
    _nextpt = 0;                // start over at 0
  }

// This example does not handle scrolling, but a derived canvas would
// be likely to. Thus, we've included the derived scrolling methods,
// but simply call the superclass method for default handling, which
// is essentially a no op.

//======================>>> tCanvasPane::HPage <<<========================
  void tCanvasPane::HPage(int shown, int top)
  {
    vCanvasPane::HPage(shown, top);
  }

//======================>>> tCanvasPane::VPage <<<========================
  void tCanvasPane::VPage(int shown, int top)
  {
    vCanvasPane::VPage(shown, top);
  }

//======================>>> tCanvasPane::HScroll <<<======================
  void tCanvasPane::HScroll(int step)
  {
    vCanvasPane::HScroll(step);
  }

//======================>>> tCanvasPane::VScroll <<<======================
  void tCanvasPane::VScroll(int step)
  {
    vCanvasPane::VScroll(step);
  }

//=====================>>> tCanvasPane::MouseDown <<<=====================
  void tCanvasPane::MouseDown(int X, int Y, int button)
  {
   // Mouse down means the user is starting a line. We don't care which
   // button was pressed. There is nothing to draw until the mouse moves.

    _mouseDown = 1;                     // track mouse button
    _pt[_nextpt].x = _begx = _curx = X; // starting point
    _pt[_nextpt].y = _begy = _cury = Y;
    if (++_nextpt >= 200)               // set next point and do a simple
        _nextpt = 0;                    // minded storage allocation
  }

//======================>>> tCanvasPane::MouseMove <<<====================
  void tCanvasPane::MouseMove(int x, int y, int button)
  {
    // Mouse move means the user is drawing a line, so we have to draw
    // it on the screen. By drawing a Rubber Line, we can easily track
    // the user motions, and undraw the previous line.

    if (_begx != _curx || _begy != _cury) // Was there a previous line?
        DrawRubberLine(_begx, _begy, _curx, _cury);  // Undraw old line

    if (_begx != x || _begy != y)       // If we moved, draw new line
        DrawRubberLine(_begx, _begy, x, y);
    
    _curx = x; _cury = y;               // update positions
  }

//========================>>> tCanvasPane::MouseUp <<<====================
  void tCanvasPane::MouseUp(int X, int Y, int button)
  {
    // Mouse up means the user has ended a line, so we need to draw
    // a permanent line and update the data base.

    _mouseDown = 0;                     // Mouse down now
    if (_begx != X || _begy != Y)       // We drew a line
        DrawLine(_begx, _begy, X, Y);   // So draw permanent version

    _pt[_nextpt].x = X; _pt[_nextpt].y = Y;  // End point

    if (++_nextpt >= 200)               // set next point and do a simple
        _nextpt = 0;                    // minded storage allocation

    _begx = -1; _begy = -1; _curx = -1; _cury = -1;  // for next line
  }

//========================>>> tCanvasPane::Redraw <<<=====================
  void tCanvasPane::Redraw(int x, int y, int w, int h)
  {
    // This is a simple Redraw that just redraws everything.
    // Often, that will be more than fast enough, but the input
    // parameters can be used to make a more intelligent redraw.

    for (int i = 0 ; i < _nextpt ; i += 2)
        DrawLine(_pt[i].x, _pt[i].y, _pt[i+1].x, _pt[i+1].y);
  }

//======================>>> tCanvasPane::Resize <<<=======================
  void tCanvasPane::Resize(int w, int h)
  {
    // We also don't handle resizing in this example.
    vCanvasPane::Resize(w,h);
  }

tcanvas.h

//========================================================================
//  tcanvas.h -- header file for tutorial canvas class
//
//      Copyright 1995, Bruce E. Wampler, All Rights Reserved.
//========================================================================

#ifndef TCANVAS_H
#define TCANVAS_H

#include <v/vcanvas.h>  // derive from vCanvasPane

    typedef struct point        // simple structure for points
      {
        int x; int y;
      } point;

    class tCanvasPane : public vCanvasPane
      {
      public:           //---------------------------------------- public
        tCanvasPane();
        virtual ~tCanvasPane();

        // Windows
        virtual void Clear();

        // Scrolling
        virtual void HPage(int, int);
        virtual void VPage(int, int);
        virtual void HScroll(int);
        virtual void VScroll(int);

        // Events
        virtual void MouseDown(int, int, int);
        virtual void MouseUp(int, int, int);
        virtual void MouseMove(int, int, int);
        virtual void Redraw(int, int, int, int); // Expose/redraw event
        virtual void Resize(int, int);          // Resize event

      protected:        //------------------------------------- protected

      private:          //--------------------------------------- private
        // Note that we try to use a leading underscore to indicate
        // private members. We aren't always consistent!
        int _mouseDown;         // track if mouse down
        int _begx; int _begy;   // starting point
        int _curx; int _cury;   // current point
        point *_pt;             // the array of points
        int _nextpt;            // where next point goes
      };
#endif

A.4 A Modeless Dialog

tdialog.cpp

//========================================================================
//  tdialog.cpp - Source file for tutorial tDialog class
//
//  Copyright 1995, Bruce E. Wampler, All Rights Reserved
//========================================================================

// #include the headers we need
#include <v/vnotice.h>
#include "tdialog.h"

// The structure of a derived dialog class is very similar to the
// structure of a command window class. First we define IDs for the
// various command objects used in the dialog. Then we declare the
// static array that defines the dialog.

const ItemVal mdLbl1 = 200;

const ItemVal mdFrm1 = 201;  const ItemVal mdLbl2 = 202;
const ItemVal mdCB1 = 203;   const ItemVal mdCB2 = 204;
const ItemVal mdCB3 = 205;

const ItemVal mdFrmV1 = 206; const ItemVal mdLbl3 = 207;
const ItemVal mdRB1 = 208;   const ItemVal mdRB2 = 209;

const ItemVal mdFrmV2 = 210; const ItemVal mdLbl4 = 211;
const ItemVal mdBtn1 = 212;  const ItemVal mdBtn2 = 213;

const ItemVal mdBtnChange = 214;

    static char change_me[] = "Change Me A";    // a label to change

// This defines the dialog

    static CommandObject DefaultCmds[] =
      {
        {C_Label, mdLbl1, 0,"X",NoList,CA_MainMsg,isSens,NoFrame, 0, 0},

        {C_Frame,mdFrmV2,0,"",NoList,CA_None,isSens,NoFrame,0,mdLbl1},
        {C_Label,mdLbl4,0,"Buttons",NoList,CA_None,isSens,mdFrmV2,0,0},
        {C_Button,mdBtn1,mdBtn1,"Button 1",NoList,CA_None,
                isSens,mdFrmV2,0,mdLbl4},
        {C_Button,mdBtn2,mdBtn2,"Button 2",NoList,CA_None,
                isSens,mdFrmV2,0,mdBtn1},

        {C_Frame,mdFrm1,0,"",NoList,CA_None,isSens,NoFrame,mdFrmV2,mdLbl1},
        {C_Label,mdLbl2,0,"CheckBox",NoList,CA_None,isSens,mdFrm1,0,0},
        {C_CheckBox,mdCB1,0,"Test A",NoList,CA_None,
                isSens,mdFrm1,0,mdLbl2},
        {C_CheckBox,mdCB2,0,"Test B",NoList,CA_None,
                isSens,mdFrm1,mdCB1,mdLbl2},
        {C_CheckBox,mdCB3,1,"Test C",NoList,CA_None,isSens,mdFrm1,0,mdCB1},

        {C_Frame,mdFrmV1,0,"",NoList,CA_None,isSens,NoFrame,mdFrm1,mdLbl1},
        {C_Label,mdLbl3,0,"Radios",NoList,CA_None,isSens,mdFrmV1,0,0},
        {C_RadioButton,mdRB1,1,"KOB",NoList,CA_None,
                isSens,mdFrmV1,0,mdLbl3},
        {C_RadioButton,mdRB2,0,"KOAT",NoList,CA_None,
                isSens,mdFrmV1,0,mdRB1},

        {C_Button,mdBtnChange,0,change_me,NoList,CA_None,
                isSens,NoFrame,0,mdFrmV1},
        {C_Button,M_Cancel,M_Cancel," Cancel ",NoList,CA_None,
            isSens,NoFrame,mdBtnChange,mdFrmV1},
        {C_Button,M_OK,M_OK," OK ",NoList,CA_DefaultButton,
            isSens,NoFrame,M_Cancel,mdFrmV1},

        {C_EndOfList,0,0,0,0,CA_None,0,0,0}
      };


//==========================>>> tDialog::tDialog <<<======================
  tDialog::tDialog(vBaseWindow* bw) :
    vDialog(bw)
  {
    // The constructor for a derived dialog calls the superclass
    // constructor, and then adds the command objects to the dialog
    // by calling AddDialogCmds.

    UserDebug(Constructor,"tDialog::tDialog()\n")
    AddDialogCmds(DefaultCmds);         // add the command objects
  }

//=========================>>> tDialog::~tDialog <<<======================
  tDialog::~tDialog()
  {
    // Destructor often doesn't need to do anything

    UserDebug(Destructor,"tDialog::~tDialog() destructor\n")
  }

//====================>>> tDialog::DialogCommand <<<======================
  void tDialog::DialogCommand(ItemVal id, ItemVal retval, CmdType ctype)
  {
    // After the user has selected a command from the dialog,
    // this routine is called with the value.

    vNoticeDialog note(this);   // an instance we can use

    UserDebug1(CmdEvents,"tDialog::DialogCommand(id:%d)\n",id)

    switch (id)         // We will do some things depending on value
      {
        case mdCB1:             // CheckBox
            note.Notice("Test A");
            break;

        case mdCB2:             // CheckBox
            note.Notice("Test B");
            break;

        case mdCB3:             // CheckBox
            note.Notice("Test C");
            break;

        case mdRB1:             // Radio Button
            note.Notice("KOB");
            break;

        case mdRB2:             // Radio Button
            note.Notice("KOAT");
            break;

        case mdBtn1:            // Button
            note.Notice("Button 1");
            break;

        case mdBtn2:            // Button
            note.Notice("Button 2");
            break;

        case mdBtnChange:       // Example: change my own label
            // We will change the label on this button
            change_me[10]++;            // change the "A"
            SetString(mdBtnChange, change_me);
            break;
      }
    // All commands should also route through the parent handler
    // which has useful default behaviors for Cancel and OK
    vDialog::DialogCommand(id,retval,ctype);
  }

tdialog.h

//========================================================================
//
//  tdialog.h - Header file for tutorial tDialog class
//
//  Copyright 1995, Bruce E. Wampler, All Rights Reserved
//========================================================================

#ifndef TDIALOG_H
#define TDIALOG_H

#include <v/vdialog.h>  // we derive from vDialog

    class tDialog : public vDialog
      {
      public:           //---------------------------------------- public
        tDialog(vBaseWindow*);
        virtual ~tDialog();             // Destructor
        virtual void DialogCommand(ItemVal id, ItemVal retval,
                CmdType ctype);

      protected:        //------------------------------------- protected

      private:          //--------------------------------------- private
        int _toggleId;
      };
#endif

A.5 A Modal Dialog

tmodal.cpp

//========================================================================
//  tmodal.cpp - Source file for tutorial tModalDialog class
//
//  Copyright 1995, Bruce E. Wampler, All Rights Reserved
//========================================================================
//

#include "tmodal.h"             // our header file
#include <v/vnotice.h>

const ItemVal mmLbl1 = 300;
const ItemVal mmBtn1 = 301;
const ItemVal mmBtn2 = 302;

    static  DefaultCmds[] =
      {
        {C_Label, mmLbl1, 0,"X",NoList,CA_MainMsg,isSens,NoFrame, 0, 0},
        
        {C_Button,mmBtn1,mmBtn1," Test 1 ",NoList,CA_None,
                isSens,NoFrame,0,mmLbl1},
        {C_Button,mmBtn2,mmBtn2," Test 2 ",NoList,CA_None,
                isSens,NoFrame, mmBtn1,mmLbl1},

        {C_Button,M_Cancel,M_Cancel," Cancel ",NoList,CA_None,
                isSens,NoFrame, 0,mmBtn1},
        {C_Button,M_OK,M_OK,"   OK   ",NoList,CA_DefaultButton,
                isSens,NoFrame,M_Cancel,mmBtn1},

        {C_EndOfList,0,0,0,0,CA_None,0,0,0}
      };


//======================>>> tModalDialog::tModalDialog <<<================
  tModalDialog::tModalDialog(vBaseWindow* bw) :
    vModalDialog(bw)
  {
    UserDebug(Constructor,"tModalDialog::tModalDialog()\n")
    AddDialogCmds(DefaultCmds);         // add the predefined commands
  }

//=================>>> tModalDialog::~tModalDialog <<<====================
  tModalDialog::~tModalDialog()
  {
    UserDebug(Destructor,"tModalDialog::~tModalDialog() destructor\n")
  }

//===================>>> tModalDialog::DialogCommand <<<==================
  void tModalDialog::DialogCommand(ItemVal id,ItemVal retval,CmdType ctype)
  {
    // After the user has selected a command from the dialog,
    // this routine is called with the id and retval.

    vNoticeDialog note(this);

    UserDebug1(CmdEvents,"tModalDialog::DialogCommand(id:%d)\n",id)

    switch (id)         // We will do some things depending on value
      {
        case mmBtn1:            // Button 1
            note.Notice(" Test 1 ");
            break;

        case mmBtn2:            // Button 2
            note.Notice(" Test 2 ");
            break;
      }

    // let default behavior handle Cancel and OK
    vModalDialog::DialogCommand(id,retval,ctype);
  }

tmodal.h

//========================================================
//  tmodal.h - Header file for tModalDialog class
//
//  Copyright 1995, Bruce E. Wampler, All Rights Reserved
//========================================================

#ifndef TMODAL_H
#define TMODAL_H

#include <v/vmodald.h>  // derived from vModalDialog

    class tModalDialog : public vModalDialog
      {
      public:           //---------------------------------------- public
        tModalDialog(vBaseWindow*);
        virtual ~tModalDialog();                // Destructor
        virtual void DialogCommand(ItemVal id, ItemVal retval,
                CmdType ctype);

      protected:        //--------------------------------------- protected

      private:          //--------------------------------------- private

      };
#endif

A.6 The Makefile

makefile

# Sample GNU make makefile for V tutorial application
CC      =       g++

# Note: Platform dependent for a Linux system

HOME    =       /home/bruce
X11INC  =       /usr/X11/include
X11LIB  =       /usr/X11R6/lib
Arch    =       intel
LIBS    =       -lV -lXaw -lXmu -lXt -lXext -lX11

VPATH   =       ../include

# Architecture dependent

VLibDir =       $(HOME)/v/lib/$(Arch)

oDir    =       ../obj/$(Arch)

LibDir  =       ../lib/$(Arch)

Bin     =       ../bin/$(Arch)

#--------------------------------------------------------------

# Typical flags for includes and libraries

CFLAGS  =       -O -I$(X11INC) -I$(HOME)

LFLAGS  =       -O -L$(X11LIB) -L$(VLibDir)

EXOBJS  =       $(oDir)/tutapp.o \
                $(oDir)/tdialog.o \
                $(oDir)/tmodal.o \
                $(oDir)/tcanvas.o \
                $(oDir)/tcmdwin.o

all:    $(Bin)/tutapp

$(Bin)/tutapp:  $(EXOBJS) $(VLibDir)/libV.a
        $(CC) -o $@ $(LFLAGS) $(EXOBJS) $(LIBS)

$(oDir)/tcanvas.o:      tcanvas.cpp v_defs.h tcanvas.h
        $(CC) -c $(CFLAGS) -o $@ $<                     

$(oDir)/tdialog.o:      tdialog.cpp v_defs.h tdialog.h
        $(CC) -c $(CFLAGS) -o $@ $<                     

$(oDir)/tmodal.o:       tmodal.cpp v_defs.h tmodal.h
        $(CC) -c $(CFLAGS) -o $@ $<                     

$(oDir)/tcmdwin.o:      tcmdwin.cpp v_defs.h tcmdwin.h
        $(CC) -c $(CFLAGS) -o $@ $<                     

$(oDir)/tutapp.o:       tutapp.cpp v_defs.h tdialog.h tmodal.h \
        tutapp.h tcmdwin.h
        $(CC) -c $(CFLAGS) -o $@ $<