### Layout Using Canvas Controls

A canvas is a window that manages its child windows. Different canvases provide a range of support, including the following:

- Managing the size and position of child windows
- Providing movable split bars between windows
- Supporting the ability to scroll a window

With the canvas classes, you can build windows with multiple child controls that contain fixed-size areas, user-sizeable areas, and scrollable areas. In addition, a canvas control lets you control tabbing between child controls, providing an alternative to using dialog boxes.

Generally, you build a complex window with a canvas control as the client area. This canvas can contain other canvas controls to build the desired layout.

The canvas classes are:

- ICanvas
- IDrawingCanvas
- IMultiCellCanvas
- ISetCanvas
- ISplitCanvas
- IToolBar
- IViewPort

The set and multicell canvases automatically size and position their child windows for you, based on the child window's minimum size.

#### Using Split Canvases

A split canvas places its child controls into panes. The panes are separated by movable or fixed split bars. (The default is movable split bars.) A split canvas can have its split bars oriented vertically or horizontally.

Note: | In Motif, you can only move the split canvas using the small square buttons. However, in Windows and OS/2, you can move a split canvas using any part of the split bar line.

Use a split canvas to contain controls that can be resized to display more information, such as list boxes, containers, MLEs, and notebooks.

Note: | Use the IListBox::noAdjustPosition style on a list box control in a split canvas because, otherwise, the operating system may adjust the height of the list box so it will not fill the height of the split canvas.

The order in which you create the child controls determines both their relative position on the split canvas and the order in which tab and cursor keys switch focus between them. For a canvas with vertical split bars, the child controls are arranged with the control that was created first in the leftmost pane. For a canvas with horizontal split bars, the control that was created first is placed in the top pane.

#### Creating a Split Canvas

The following example shows you how to create a split canvas.

1. Declare a split canvas in the .hpp file, as follows:

       /***********************************************************/
       /* MySplit class - split canvas class and associated       */
       /* controls                                                */
       /***********************************************************/
       class MySplit : public ISplitCanvas {
         public:
           MySplit(unsigned long winId, IWindow* pParent);

         private:
           IStaticText  statTxt1;
           IStaticText  statTxt2;
           IStaticText  statTxt3;
       };

       /***********************************************************/
       /* Command handler declaration                             */
       /***********************************************************/
       class ACommandHandler : public ICommandHandler {

       public:
         ACommandHandler(AppWindow *asplcan);

       protected:
         virtual bool command(ICommandEvent& cmdEvent);

       private:
         AppWindow *splcan;
       };

       /***********************************************************/
       /* AppWindow class declaration                             */
       /***********************************************************/
       class AppWindow : public IFrameWindow
        {
         public:
           AppWindow( unsigned long windowId);
           ~AppWindow();
           AppWindow & updateCanvas(unsigned long eventtype);

         private:

           ITitle           *   title;
           MySplit          *   splitCv;
           ACommandHandler  *   commandHandler;
        };

2. Create the window constructor in a .cpp file, as follows:

       /***********************************************************/
       /* Window constructor                                      */
       /***********************************************************/

       AppWindow :: AppWindow( unsigned long windowId)
          : IFrameWindow(windowId,                 // create Frame window
                         defaultStyle() | menuBar)
       {

       //**********************************************************/
       // Create title                                            */
       //**********************************************************/
       title = new ITitle(this,PSZ_OBJECT,PSZ_VIEW);

       //**********************************************************/
       // Create split canvas                                     */
       //**********************************************************/
       splitCv = new MySplit(ID_SPLIT, this);
       setClient(splitCv);

       //*********************************************************
       // Create command handler
       //*********************************************************
       commandHandler = new ACommandHandler(this);
       commandHandler->handleEventsFor(this);
       }

       /***********************************************************/
       /*          TestWindow Destructor                          */
       /***********************************************************/

       AppWindow :: ~AppWindow ()
       {
          commandHandler->stopHandlingEventsFor(this);
          delete title;
          delete splitCv;
       }

       /***********************************************************/
       /*            Perform Menu Events                          */
       /***********************************************************/
       AppWindow & AppWindow::updateCAN(unsigned long eventtype)
       {
          switch (eventtype)
          {
             case ID_VERT_ITEM:
                 // Change to vertical orientation
                 splitCv->setOrientation(ISplitCanvas::verticalSplit);
                 splitCv->refresh();
                 return (*this);

             case ID_HORIZ_ITEM:
                 // Change to horizontal orientation
                 splitCv->setOrientation(ISplitCanvas::horizontalSplit);
                 splitCv->refresh();
                 return (*this);

             case ID_DOUBLE_EDGE_ITEM:
                 // Double the split bar edge
                 {
                   unsigned long thickness;
                   thickness = splitCv->splitBarThickness(ISplitCanvas::splitBarEdge);
                   splitCv->setSplitBarThickness(ISplitCanvas::splitBarEdge, thickness*2);
                   splitCv->refresh();
                   return (*this);
                 }

             case ID_HALVE_EDGE_ITEM:
                 // Halve the split bar edge
                 {
                   unsigned long thickness;
                   thickness = splitCv->splitBarThickness(ISplitCanvas::splitBarEdge);
                   splitCv->setSplitBarThickness(ISplitCanvas::splitBarEdge, thickness/2);
                   splitCv->refresh();
                   return (*this);
                 }

             case ID_DOUBLE_MIDDLE_ITEM:
                 // Double the split bar middle
                 {
                   unsigned long thickness;
                   thickness = splitCv->splitBarThickness(ISplitCanvas::splitBarMiddle);
                 splitCv->setSplitBarThickness(ISplitCanvas::splitBarMiddle, thickness*2);
                   splitCv->refresh();
                   return (*this);
                 }

             case ID_HALVE_MIDDLE_ITEM:
                 // Halve the split bar middle
                 {
                 unsigned long thickness;
                 thickness = splitCv->splitBarThickness(ISplitCanvas::splitBarMiddle);
                 splitCv->setSplitBarThickness(ISplitCanvas::splitBarMiddle, thickness/2);
                 splitCv->refresh();
                 return (*this);
                 }

          } /* end switch */
          return (*this);
       }

       /***********************************************************/
       /*            Construct the Command Handler                */
       /***********************************************************/
       ACommandHandler::ACommandHandler(AppWindow *asplcnv)
       {
         splcan = asplcnv;
       }

       /***********************************************************/
       /*            MyWindow Command Event Handler               */
       /***********************************************************/
       bool ACommandHandler :: command(ICommandEvent& cmdEvent)
        {
          switch (cmdEvent.commandId())
          {
             case ID_VERT_ITEM:
             case ID_HORIZ_ITEM:
             case ID_DOUBLE_EDGE_ITEM:
             case ID_HALVE_EDGE_ITEM:
             case ID_DOUBLE_MIDDLE_ITEM:
             case ID_HALVE_MIDDLE_ITEM:
              splcan->updateCAN(cmdEvent.commandId());
              break;
              return true;
          }
          return false;
       }

       /***********************************************************/
       /* MySplit constructor                                     */
       /***********************************************************/
       MySplit :: MySplit(unsigned long winId, IWindow* pParent)
        : ISplitCanvas(winId, pParent, pParent),
          statTxt1(ID_TEXT1, this, this),
          statTxt2(ID_TEXT2, this, this),
          statTxt3(ID_TEXT3, this, this)
       {
          setSplitWindowPercentage(&statTxt1, 20);
          setSplitWindowPercentage(&statTxt2, 40);
          setSplitWindowPercentage(&statTxt3, 40);
          statTxt1.setText(PSZ_TEXT1);
          statTxt2.setText(PSZ_TEXT2);
          statTxt3.setText(PSZ_TEXT3);
          statTxt1.setBackgroundColor(IColor::kRed);
          statTxt2.setBackgroundColor(IColor::kWhite);
          statTxt3.setBackgroundColor(IColor::kBlue);
          statTxt3.setForegroundColor(IColor::kWhite);
       }

3. Handle command events:

       /***********************************************************/
       /* Construct the command handler                           */
       /***********************************************************/
       ACommandHandler::ACommandHandler(AppWindow *asplcnv)
       {
         splcan = asplcnv;
       }

       /***********************************************************/
       /* MyWindow command event handler                          */
       /***********************************************************/
       bool ACommandHandler :: command(ICommandEvent& cmdEvent)
        {
          switch (cmdEvent.commandId())
          {
             case ID_VERT_ITEM:
             case ID_HORIZ_ITEM:
             case ID_DOUBLE_EDGE_ITEM:
             case ID_HALVE_EDGE_ITEM:
             case ID_DOUBLE_MIDDLE_ITEM:
             case ID_HALVE_MIDDLE_ITEM:
              splcan->updateCanvas(cmdEvent.commandId());
              return true;
          }
          return false;
       }

The following figure shows the split canvas created by using the preceding example:

The following examples show how to create a window containing two split canvases. Each pane is occupied by a static text control.

1. This code from the header file declares the ASplitCanvas class as an IFrameWindow derived class.

   Note: | Member functions are initialized in the order that they appear in the class declaration.

       #include <iframe.hpp>                            // IFrameWindow #include <istattxt.hpp>                          // IStaticText #include <isplitcv.hpp>                          // ISplitCanvas class ASplitCanvas : public IFrameWindow {   public:     ASplitCanvas(unsigned long windowId);        // Constructor   private:.
           ISplitCanvas  horzCanvas,              // The canvases will be created
                         vertCanvas;              // in the same order they
           IStaticText   lText,                         // are declared.
                         rText,
                         bText;
       };

2. This code creates the window:

        1 ASplitCanvas :: ASplitCanvas( unsigned long windowId )
        2   : IFrameWindow( windowId )
        3   , horzCanvas( WND_CANVAS, this, this )
        4   , vertCanvas( WND_CANVAS2, &horzCanvas, &horzCanvas )
        5   , lText( WND_TXTL, &vertCanvas, &vertCanvas )
        6   , rText( WND_TXTR, &vertCanvas, &vertCanvas )
        7   , bText( WND_TXTB, &horzCanvas, &horzCanvas )
        8 {
        9
       10   horzCanvas.setOrientation( ISplitCanvas::horizontalSplit ); //Give the canvas
       11   setClient( &horzCanvas );          //a horizontal split bar
       12                                      //and make it the client area
       13
       14   vertCanvas.setOrientation( ISplitCanvas::verticalSplit );//Give the canvas
       15                                      //a vertical split bar
       16   lText.setText(STR_TOPLEFT);        //Set top left static text
       17   lText.setAlignment( IStaticText::centerCenter );
       18
       19   rText.setText(STR_TOPRIGHT);       //Set top right static text
       20   rText.setAlignment( IStaticText::centerCenter );
       21
       22   bText.setText(STR_BOTTOM);         //Set bottom static text
       23   bText.setAlignment( IStaticText::centerCenter );
       24
       25   setFocus().show();                 //Set focus and show window
       26
       27 } /* end ASplitCanvas :: ASplitCanvas(...) */

Lines 1 through 7 create a canvas with horizontal and vertical split bars. The canvases are created in the same order they were declared in the header file. In line 4, the vertical canvas is defined as a child of the horizontal canvas.

Lines 10 and 11 make the horizontal canvas the client window.

Line 14 defines a canvas with vertical split bars.

Lines 16 through 23 set the text for each static control and position the text in each pane.

The following figure shows the completed split canvas:

#### Using Set Canvases

A set canvas arranges its child controls in either rows or columns. The Open Class Class Library uses the term deck for either a row or column. You can arrange the decks of a set canvas either horizontally or vertically. The set canvas attempts to place the same number of controls in each deck.

Each deck is created large enough to contain the largest control in the deck. To do this, the canvas calls the minimumSize member function for each child control. For controls that have sizes defined by the text they contain, such as push buttons and radio buttons, this default processing is normally sufficient. However, for a control that does not have a well-defined size, such as a list box or multiline edit control, you need to set its minimum size by overriding the calcMinimizeSize member function or by calling its setMinimumSize member function before adding it to the set canvas.

Note: | Your application can determine the best minimum size for list boxes, multiline edit controls, containers, and frame windows.

The order in which you create the child controls determines their positions on the set canvas and the order in which tab and cursor keys switch focus between the controls. Several styles are available to control the orientation of the decks and the placement of controls within the decks. You can also alter the spacing between controls and between the decks and the edge of the canvas.

#### Creating a Set Canvas

The following example shows you how to create a set canvas. This sample demonstrates the use of horixontal and veritical decks.

1. Declare the classes that use a set canvas, as follows:

       /******************************************************************************/
       /* Class   : AButtonHandler                                                   */
       /*                                                                            */
       /* Purpose : this is the select handler for the radio buttons.                */
       /*           The button handler is called when a radio button is selected.    */
       /*           It displays the number of the button in the static text that     */
       /*           is given to it using useInfoArea member function.                */
       /*           AButtonHandler is derived from ISelectHandler                    */
       /******************************************************************************/
       class AButtonHandler : public ISelectHandler
       {
       public:
          AButtonHandler() : output(0) {;}
          useStatus(IStaticText * pstatus) { output = pstatus; }
       protected:
       bool
         selected      ( IControlEvent& evt );
       private:
           IStaticText   * output;
       };

       /******************************************************************************/
       /* Class   : ASetCanvas                                                       */
       /*                                                                            */
       /* Purpose : main window for set canvas sample                                */
       /*           the window has a split canvas as the client area.                */
       /*           Three other controls are added to the split canvas. A static     */
       /*           text control is added in the top pane, the lower two panes       */
       /*           contain set canvases.                                            */
       /*           ASetCanvas is derived from IFrameWindow                          */
       /******************************************************************************/
       class ASetCanvas : public IFrameWindow
       {
         public:
           ASetCanvas(unsigned long windowId);                   // constructor
          ~ASetCanvas();                                         // destructor

         private:
           ISplitCanvas    clientCanvas;
           IStaticText     status;
           ISetCanvas      vSetCanvas,
                           hSetCanvas;
           IRadioButton  * radiobut[NUMBER_OF_BUTTONS];
           AButtonHandler  buttonHandler;
       };

2. Define the class and its children in the .cpp file, as follows:

       /******************************************************************************/
       /* ASetCanvas :: ASetCanvas - Constructor for our main window                 */
       /******************************************************************************/
       ASetCanvas::ASetCanvas(unsigned long windowId)
         : IFrameWindow( windowId )
         , clientCanvas( WND_SPLITCANVAS, this, this ,IRectangle(),
                         ISplitCanvas::horizontal | IWindow::visible)
         , status( WND_STATUS, &clientCanvas, &clientCanvas )
         , vSetCanvas( WND_VSETCANVAS, &clientCanvas, &clientCanvas )
         , hSetCanvas( WND_HSETCANVAS, &clientCanvas, &clientCanvas )
       {
                                              // make split canvas the client area
         setClient( &clientCanvas );
                                              // set alignment of status area text
         status.setAlignment( IStaticText::centerCenter );

                                              // top canvas has 3 vertical decks
         vSetCanvas.setDeckOrientation( ISetCanvas::vertical );
         vSetCanvas.setDeckCount( 3 );
                                              // bottom canvas has 3 horizontal decks
         hSetCanvas.setDeckOrientation( ISetCanvas::horizontal );
         hSetCanvas.setDeckCount( 3 );
         hSetCanvas.setPad(ISize(10,10));    // set some space around buttons

         buttonHandler.useStatus( &status ); // give button handler a text control

         unsigned int i, mid = (NUMBER_OF_BUTTONS/2);
                                             // create the first set of radio buttons
         for (i = 0 ; i setText( STR_TEXT + i );
            }
         radiobut[0]->enableGroup().enableTabStop();   // set tabStop and Group styles
         radiobut[0]->select();                        // select first button in group

                                             // create the second set of radio buttons
         for (i = mid ; i setText( STR_TEXT + i );
            }
         radiobut[mid]->enableGroup().enableTabStop(); // set tabStop and Group styles
         radiobut[mid]->select();                      // select first button in group

         radiobut[0]->setFocus();             // set focus to radio button one
         status.setText( STR_STATUS );        // set status area text from resource

         show();                              // show main window

       } /* end ASetCanvas :: ASetCanvas(...) */

       /******************************************************************************/
       /* ASetCanvas ::~ASetCanvas - Destructor for our main window                  */
       /******************************************************************************/
       ASetCanvas::~ASetCanvas()
       {
          for (unsigned int i = 0; i = WND_BUTTON &&
               ulButtonId <= (wnd_button+number_of_buttons) && output) output->setText( IString( ulButtonId-WND_BUTTON+1 ) );
          else
             fHandled = false;                 // pass event to other handlers

          return fHandled;

       } /* end AButtonHandler::selected(...) */

The following figure shows a set canvas created by using the preceding example:

The following sample uses a split canvas as a client area. Two set canvases, each with seven radio buttons, are then added to the split canvas.

1. This code from the header file, asetcv.hpp, declares the ASetCanvas class as an IFrameWindow derived class:

       #ifndef ASETCV_HPP
       #define ASETCV_HPP

       #define MAX_BUTTONS  7

       #include <iframe.hpp>                   // IFrameWindow
       #include <iradiobt.hpp>                 // IRadioButton
       #include <ipushbut.hpp>                 // IPushButton
       #include <isetcv.hpp>                   // ISetCanvas
       #include <isplitcv.hpp>                 // ISplitCanvas

       class ASetCanvas : public IFrameWindow {   public:                               //Define the public information     ASetCanvas(unsigned long windowId); //Constructor for this class    ~ASetCanvas();                       //Destructor for this class   private:                              //Define private information     ISplitCanvas    clientCanvas;     IStaticText     status;     ISetCanvas      vSetCanvas,                     hSetCanvas;     IRadioButton  * radiobut[NUMBER_OF_BUTTONS];     AButtonHandler  buttonHandler; };.

2. This code from asetcv.cpp creates the window:

       ASetCanvas::ASetCanvas(unsigned long windowId)
         : IFrameWindow( windowId )
         /*---------------------------------------------------------------------------|
         |  Create the ISplitCanvases                                                 |
         ----------------------------------------------------------------------------*/
         , horzCanvas1( ID_HORIZCANVAS1, this, this )
         , vertCanvas1( ID_VERTCANVAS1,  &horzCanvas1, &horzCanvas1 )
         , vertCanvas2( ID_VERTCANVAS2,  &horzCanvas1, &horzCanvas1 )
         , vertCanvas3( ID_VERTCANVAS3,  &horzCanvas1, &horzCanvas1 )
         /*---------------------------------------------------------------------------|
         |  Create the ISetCanvases                                                   |
         ----------------------------------------------------------------------------*/
         , setCanvas1( ID_SETCANVAS1, &vertCanvas1, &vertCanvas1 )
         , setCanvas2( ID_SETCANVAS2, &vertCanvas1, &vertCanvas1 )
         , setCanvas3( ID_SETCANVAS3, &vertCanvas2, &vertCanvas2 )
         , setCanvas4( ID_SETCANVAS4, &vertCanvas2, &vertCanvas2 )
         , setCanvas5( ID_SETCANVAS5, &vertCanvas3, &vertCanvas3 )
         , setCanvas6( ID_SETCANVAS6, &vertCanvas3, &vertCanvas3 )
         /*---------------------------------------------------------------------------|
         |  Create the radio buttons for each ISetCanvases                            |
         ----------------------------------------------------------------------------*/
         , vRadioGroup1( ID_VRADIO,      &setCanvas1, 5, AButtons::vertical )
         , vRadioGroup2( ID_VRADIO + 5,  &setCanvas3, 6, AButtons::vertical )
         , vRadioGroup3( ID_VRADIO + 11, &setCanvas5, 7, AButtons::vertical )
         , hRadioGroup1( ID_HRADIO,      &setCanvas2, 5, AButtons::horizontal )
         , hRadioGroup2( ID_HRADIO + 5,  &setCanvas4, 6, AButtons::horizontal )
         , hRadioGroup3( ID_HRADIO + 11, &setCanvas6, 7, AButtons::horizontal )
       {
       /*-----------------------------------------------------------------------------|
       |  Set the icon for the main window and make the split canvas the client area  |
       ------------------------------------------------------------------------------*/
         setIcon( id() );
         setClient( &horzCanvas1 );

       /*-----------------------------------------------------------------------------|
       |  Set the split canvas orientation                                            |
       ------------------------------------------------------------------------------*/
         horzCanvas1.setOrientation( ISplitCanvas::horizontalSplit );
         vertCanvas1.setOrientation( ISplitCanvas::verticalSplit );
         vertCanvas2.setOrientation( ISplitCanvas::verticalSplit );
         vertCanvas3.setOrientation( ISplitCanvas::verticalSplit );

       /*-----------------------------------------------------------------------------|
       |  Set three ISetCanvases to have 3 vertical decks                             |
       ------------------------------------------------------------------------------*/
         setCanvas1.setDeckOrientation( ISetCanvas::vertical );
         setCanvas3.setDeckOrientation( ISetCanvas::vertical );
         setCanvas5.setDeckOrientation( ISetCanvas::vertical );

       /*-----------------------------------------------------------------------------|
       |  Set three ISetCanvases to have 3 horizontal decks                           |
       ------------------------------------------------------------------------------*/
         setCanvas2.setDeckOrientation( ISetCanvas::horizontal );
         setCanvas4.setDeckOrientation( ISetCanvas::horizontal );
         setCanvas6.setDeckOrientation( ISetCanvas::horizontal );

       /*-----------------------------------------------------------------------------|
       |  Set each ISetCanvases to have 3 decks                                       |
       ------------------------------------------------------------------------------*/
         setCanvas1.setDeckCount( 3 );
         setCanvas2.setDeckCount( 3 );
         setCanvas3.setDeckCount( 3 );
         setCanvas4.setDeckCount( 3 );
         setCanvas5.setDeckCount( 3 );
         setCanvas6.setDeckCount( 3 );

         show();

       } /* end ASetCanvas :: ASetCanvas */

   We create the split and set canvases and make the horizontal split canvas the client area.

   We then set the deck orientation and deck count for each set canvas.

The following figure shows an example of a horizontal and vertical set canvas, each with three decks. using this code:

#### Using Multicell Canvas

A multicell canvas consists of a grid of rows and columns and looks like a spreadsheet. You place child controls on the canvas by specifying the starting cell and the number of contiguous rows and columns that they can span. You can refer to cells in the grid by the column and row value. The top left cell coordinate is (1,1).

The default cell size is 10 pixels high by 10 pixels wide. The actual number of rows and columns in the canvas is the highest row and column value used. For example, a radio button is placed at (4,5) and a push button at (2,7). Therefore, the canvas has 4 columns and 7 rows. Columns and rows can also be referenced by setColumnWidth and setRowHeight.

The initial size of a row or column is determined by the size of the largest control in that row or column. By default, the row and column sizes are fixed. You can make the rows and columns expandable by using setColumnWidth and setRowHeight (setting the parameter expandable=true), in which case sizing the canvas also sizes them.

The following figure shows a multicell canvas. Notice that the small window's location is at column 4, row 5 (4,5), while the large window starts at column 9, row 2 (9,2). The large window is 2 columns wide and 3 rows long.

Note: | A child window in a fixed-sized row and column is automatically sized based on its minimum size, which is typically based on the window's content and the available area within the window.

You can also leave rows and columns empty to provide spacing between child controls. If you do not explicitly size the empty rows and columns using setRowHeight and setRowWidth, they are sized to the default cell size.

You cannot have more than one window occupy the same "starting cell". (Starting cell means the start row and column you specify when you call addToCell.) However, you can have overlapping cells.

The size of a row or column is initially established by calculating the minimum size needed to hold all the windows in the row or column. In practice, this means the width of a column is set to the minimum width of the largest window in the column. The width is determined by calling IWindow::minimumSize on all windows in the column. The application can set the width or height of a column or row, respectively. If an attempt is made to set a value less than the minimum size of the largest window, the value is saved but ignored because the minimum size is the larger of the hard-coded size for the cell and the minimum size of the window. In other words, the application can reserve more space than the window needs but cannot cause the clipping of the window to occur.

Refer to the Open Class Library Reference for more information about IWindow::minimumSize.

A column or row never has a smaller width or height than the minimum size of the largest window in the column or row. If the windows later calculate a smaller minimum size, the width or height of the column or row is the larger of the following:

- The new minimum size of the largest window in the column or row
- The column width or row height set by the application

The minimum size of the canvas is based on the minimum sizes of all the windows on the canvas plus the size of empty rows and columns. If the area allotted to the canvas is less than this size, the canvas is clipped at the lower right corner. You can use the IViewPort class to add scroll bars to handle this situation.

The multicell canvas has two styles that can help you create layouts with a multicell canvas by providing a way to easily visualize the placement and size of each child window. The gridLines style causes the multicell canvas to have grid lines between the rows and columns of the canvas. Grid lines are stationary. If you want grid lines that you can move with the mouse, use the dragLines style, which causes the multicell canvas to have draggable grid lines between the rows and columns of the canvas.

Here are some considerations when using grid lines and drag lines:

- Grid lines and drag lines are placed at the left and top edges of cells.
- Child windows can overwrite grid lines and drag lines.
- The use of grid lines or drag lines does not change the initial placement or sizing of child windows.
- Moving a drag line causes the child windows on each side of the drag line to be resized in a manner similar to that of an ISplitCanvas.

#### Creating a Multicell Canvas

The following example shows you how to create a multicell canvas.

1. Declare a class that uses a multicell canvas control in the .hpp file with static text and entry field controls, as follows:

       /***********************************************************/
       /* MyWindow class declaration                              */
       /***********************************************************/
       class MyWindow : public IFrameWindow { public:
             MyWindow(unsigned long windowID);
             ~MyWindow();

       protected:
             bool command(ICommandEvent& cmdEvent);

       private:
             IMultiCellCanvas      myClient;
             IStaticText           stName,
                                   stAddress,
                                   stId,
                                   stBirthday,
                                   stPhone;
             IEntryField           efName,
                                   efAddress,
                                   efId,
                                   efBirthday,
                                   efPhone;
             ACommandHandler      * commandHandler;
       };

2. Construct the multicell canvas in the .cpp file, as follows:

       MyWindow::MyWindow( unsigned long windowId)
         : IFrameWindow("MultiCell Canvas Example",windowId,
                         IFrameWindow::defaultStyle(),
           myClient( ID_MCC, this, this),
           stName( ID_ST_NAME,  &myClient, &myClient ),
           stAddress( ID_ST_ADDR,  &myClient,&myClient ),
           stId( ID_ST_ID,   &myClient, &myClient ),
           stBirthday( ID_ST_BD,   &myClient, &myClient ),
           stPhone( ID_ST_PHONE, &myClient, &myClient ),
           efName( ID_EF_NAME,  &myClient, &myClient ),
           efAddress( ID_EF_ADDR,  &myClient, &myClient ),
           efId( ID_EF_ID,    &myClient, &myClient ),
           efBirthday( ID_EF_BD,    &myClient, &myClient ),
           efPhone( ID_EF_PHONE, &myClient, &myClient )
         {

          setClient( &myClient );

       //*********************************************************
       // Build multicell canvas with fields
       //*********************************************************
         myClient.addToCell( &stName, 2, 2 );
         myClient.addToCell( &efName, 3, 2 );
         myClient.addToCell( &stAddress, 2, 3 );
         myClient.addToCell( &efAddress, 3, 3 );
         myClient.addToCell( &stId, 2, 4 );
         myClient.addToCell( &efId, 3, 4 );
         myClient.addToCell( &stBirthday, 2, 5 );
         myClient.addToCell( &efBirthday, 3, 5 );
         myClient.addToCell( &stPhone, 2, 6 );
         myClient.addToCell( &efPhone, 3, 6 );

       //*********************************************************
       // Load the static text fields
       //*********************************************************
         stName.setText( "Name:" );
         stName.setForegroundColor(IColor::yellow);
         stName.setAlignment( IStaticText::centerRight );
         stAddress.setText( "Address:" );
         stAddress.setForegroundColor(IColor::yellow);
         stAddress.setAlignment( IStaticText::centerRight );
         stBirthday.setText( "Birthdate:" );
         stBirthday.setForegroundColor(IColor::yellow);
         stBirthday.setAlignment( IStaticText::centerRight );
         stId.setText( "ID:");
         stId.setForegroundColor(IColor::yellow);
         stId.setAlignment( IStaticText::centerRight );
         stPhone.setText( "Phone:");
         stPhone.setForegroundColor(IColor::yellow);
         stPhone.setAlignment( IStaticText::centerRight );

       }

The following examples show you how to create a window containing a multicell canvas. The canvas contains two check boxes, two radio buttons, three static text controls, and one push button.

1. This code from the header file declares the AMultiCellCanvas class as an IFrameWindow derived class:

       #include <iframe.hpp>                         // IFrameWindow #include <istattxt.hpp>                       // IStaticText #include <ipushbut.hpp>                       // IPushButton #include <iradiobt.hpp>                       // IRadioButton #include <icheckbx.hpp>                       // ICheckBox #include <imcelcv.hpp>                        // IMultiCellCanvas class AMultiCellCanvas : public IFrameWindow {   public:     AMultiCellCanvas(unsigned long windowId);   private:     IMultiCellCanvas    clientCanvas;     IStaticText         status,                         title1,                         title2;     ICheckBox           check1,                         check2;     IRadioButton        radio1,                         radio2;     IPushButton         pushButton; };.

2. This code creates the window:

        1 AMultiCellCanvas::AMultiCellCanvas(unsigned long windowId)
        2   : IFrameWindow(windowId)
        3   , clientCanvas( WND_MCCANVAS, this, this )
        4   , status( WND_STATUS, &clientCanvas, &clientCanvas )
        5   , title1( WND_TITLE1, &clientCanvas, &clientCanvas )
        6   , title2( WND_TITLE2, &clientCanvas, &clientCanvas )
        7   , check1( WND_CHECK1, &clientCanvas, &clientCanvas )
        8   , check2( WND_CHECK2, &clientCanvas, &clientCanvas )
        9   , radio1( WND_RADIO1, &clientCanvas, &clientCanvas )
       10   , radio2( WND_RADIO2, &clientCanvas, &clientCanvas )
       11   , pushButton( WND_PUSHBUT, &clientCanvas, &clientCanvas )
       12 {
       13
       14   setClient( &clientCanvas );      // make multicell canvas the client area
       15   status.setAlignment( IStaticText::centerCenter );// set status area text
       16   status.setText( STR_STATUS );
       17
       18   title1.setAlignment( IStaticText::centerLeft );    // set text and alignment
       19   title1.setText( STR_TITLE1 );
       20
       21   title2.setAlignment( IStaticText::centerLeft );    // set text and alignment
       22   title2.setText( STR_TITLE2 );
       23
       24   check1.setText( STR_CHECK1 );                 // set check box text
       25   check2.setText( STR_CHECK2 );
       26   radio1.setText( STR_RADIO1 );                 // set radio button text
       27   radio2.setText( STR_RADIO2 );
       28
       29   pushButton.setText( STR_PUSHBUT );
       30
       31   radio2.select();                      // preselect one radio button
       32   check1.enableGroup().enableTabStop();// set tabStop and group styles
       33   radio1.enableGroup().enableTabStop();
       34   pushButton.enableGroup().enableTabStop();
       35
       36   clientCanvas.addToCell(&status  , 1, 1, 4, 1);     // add controls to canvas.
       37   clientCanvas.addToCell(&title1  , 1, 3, 2, 1);     // the canvas runs from
       38   clientCanvas.addToCell(&title2  , 3, 3, 2, 1);     // 1,1 to 4,7
       39   clientCanvas.addToCell(&check1  , 2, 4);           // only one row and
       40   clientCanvas.addToCell(&check2  , 2, 5);           // one column are
       41   clientCanvas.addToCell(&radio1  , 4, 4);           // expandable, as this
       42   clientCanvas.addToCell(&radio2  , 4, 5);           // allows the canvas to
       43   clientCanvas.addToCell(&pushButton , 2, 7);        // fill the client area.
       44
       45   clientCanvas.setRowHeight(2, 20, true); // make row 2 20 pixels high and expandable
       46
       47   clientCanvas.setRowHeight(6, 40); // make row 6 40 pixels high
       48
       49   clientCanvas.setColumnWidth(4, 40, true);  // make column 4 40 pixels wide and expandable
       50
       51   check1.setFocus();                         // set focus to first check box
       52   show();                                    // show main window
       53
       54 } /* end AMultiCellCanvas :: AMultiCellCanvas(...) */

Note: | There is not a 1:1 relationship between Windows, OS/2 and Motif pixels.

Line 3 creates a multicell canvas.

Line 14 makes it the client area.

Lines 36 through 43 place the other controls on the canvas using the addToCell member function.

Lines 45 through 49 set the sizes for rows 2 and 6 and column 4. Row 2 and column 4 are expandable.

The following figure shows the completed multicell canvas:

#### Using View Ports

A view port canvas provides a scrollable view area with horizontal and vertical scroll bars. By default, the scroll bars display only when needed. A view port can have only one child control. The size of the child control is fixed. If the view port is smaller than the child control, the view port allows the user to scroll the child control.

If you need more than one control in a view port, place the controls into another type of canvas, which you can then make the child of the view port.

#### Creating a View Port

The following example shows how to create a view port.

1. Declare a view port in the .hpp file, as follows:

        /***********************************************************/
        /* AViewWindow declaration                                 */
        /***********************************************************/
        class AViewWindow : public IFrameWindow {  public:

           AViewWindow(unsigned long windowId);
           ~AViewWindow();
           AViewWindow & moveHoriz();
           AViewWindow & moveVert();
        private:
           ITitle          title;
           IViewPort       viewPort;
           ICanvas         canvas;
           IStaticText     text;
           IMultiLineEdit  mle1;
           IMultiLineEdit  mle2;
           ACommandHandler *commandHandler;
       };

2. Define the view port in the .cpp file as shown in the following code:

       /*************************************************************/
       /* Window constructor                                        */
       /*************************************************************/
       AViewWindow::AViewWindow(unsigned long windowId)
          : IFrameWindow(windowId,
            IFrameWindow::defaultStyle() |
            IFrameWindow::menuBar),
            title(this, "View Port Example"),
            viewPort(WID_VIEWPORT, this,this, IRectangle()),
            canvas(WID_CANVAS, &viewPort, &viewPort, IRectangle(5, 5, 410, 460)),
            text(WID_TEXT, &canvas, &canvas, IRectangle(10, 360, 400, 410)),
            mle1(WID_MLE1, &canvas, &canvas, IRectangle(10, 0, 400, 149)),
            mle2(WID_MLE2, &canvas, &canvas, IRectangle(10, 160, 400, 349))

       {

         viewPort.setColor(IViewPort::fillBackground, IColor::kYellow);
         setClient(&viewPort);
         text.setText("Enter some data into the MLE, please");

       //*************************************************************
       // Command Handler
       //*************************************************************
         commandHandler = new ACommandHandler(this);
         commandHandler->handleEventsFor(this);
         sizeTo(ISize(500,300));
         show();

       }

       AViewWindow::~AViewWindow()
       {
         commandHandler->stopHandlingEventsFor(this);
         delete commandHandler;
       }

3. Handle events, as follows:

       /*************************************************************/
       /* Horizontal move event handler                             */
       /*************************************************************/
       AViewWindow & AViewWindow :: moveHoriz()
        {
             viewPort.scrollViewHorizontallyTo(200);
             return(*this);
        }

       /*************************************************************/
       /* Vertical move event handler                               */
       /*************************************************************/
       AViewWindow & AViewWindow :: moveVert()
        {
             viewPort.scrollViewVerticallyTo(200);
             return(*this);
        }

       /*************************************************************/
       /* Command handler                                           */
       /*************************************************************/
       ACommandHandler::ACommandHandler(AViewWindow *viewWindow)
       {
         view = viewWindow;
       }

       /*************************************************************/
       /* Command handler                                           */
       /*************************************************************/
       bool ACommandHandler::command(ICommandEvent & cmdEvent)
       {
         bool eventProcessed(true);

         switch (cmdEvent.commandId())
         {
           case ID_HORIZ:
             view->moveHoriz();
             break;
           case ID_VERT:
             view->moveVert();
             break;
           default:
            eventProcessed = false;
         }
        return true;
       }

The following figure shows the view port created with the preceding code.

Canvas Controls

Using List and Slider Controls
Creating and Using Text Controls

ICanvas
IMultiCellCanvas
ISetCanvas
ISplitCanvas
IViewPort