/******************************************************************************
FILE:  WhiteBoard.c

SERVICES:  AEE WhiteBoard Drawing example application

GENERAL DESCRIPTION:
  WhiteBoard is drawing program which allows the user to render simple
  geometric shapes to the phone's display.  The user can select shapes,
  set their border, and in some cases, their fill color.  A cursor is
  used to plot points on the screen which are used to draw the selected
  geometric shape.

  This is a sample application to be used as a model by developers in
  developing BREW applications.  The application demonstrates proper
  initialization and freeing of application objects and resources, 
  event handling and GUI development.

  Several BREW API interfaces are demonstrated by the application.  These
  include the interfaces for IGraphics, IBitmap, and IDisplay.  A custom
  control CRGBCtl is also implemented to demonstrate the development
  of controls with application specific functionality.

PUBLIC CLASSES AND STATIC FUNCTIONS:
	Classes:
    IBitmap
    IGraphics
    IDisplay
    IMenuCtl
    IStatic
    IFile

INITIALIZATION & SEQUENCING REQUIREMENTS:

	See Exported Routines

       Copyright © 2002 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
******************************************************************************/

//*****************************************************************************
// INCLUDE FILES
//*****************************************************************************

#include "WhiteBoard.h"
#include "AEEShell.h"
#include "nmdef.h"

//*****************************************************************************
// FUNCTION DECLARATIONS
//*****************************************************************************

static boolean WB_InitAppData( CWhiteBoardApp* pi );
static void WB_FreeAppData( CWhiteBoardApp* pApp );

static boolean WB_HandleEvent( CWhiteBoardApp* pApp, AEEEvent eCode, uint16 wParam, uint32 dwParam );
static boolean WB_HandleAVKSelectKeyEvent( CWhiteBoardApp* pApp );

static void WB_DisplaySplashScreen( CWhiteBoardApp* pApp );
static void WB_DisplayMainScreen( CWhiteBoardApp* pApp );
static void WB_DisplayColorScreen( CWhiteBoardApp* pApp );
static void WB_DisplayInfoScreen( CWhiteBoardApp* pApp, uint16 nTitleID, uint32 nText, boolean bIsFile );
static void WB_DisplayDrawScreen( CWhiteBoardApp* pApp );

static void WB_DisplayMainMenu( CWhiteBoardApp* pApp );
static void WB_DisplayOptionMenu( CWhiteBoardApp* pApp );
static void WB_DisplayShapeMenu( CWhiteBoardApp* pApp );
static void WB_DisplayLineMenu( CWhiteBoardApp* pApp );
static void WB_DisplayFilledShapeMenu( CWhiteBoardApp* pApp );
static void WB_DisplayUnfilledShapeMenu( CWhiteBoardApp* pApp );
static void WB_DisplayControl( CWhiteBoardApp* pApp, uint16 nTitleID, void* pCtl, uint16 nX, uint16 nY, uint16 nWidth );
static void WB_DisplaySampleShape( CWhiteBoardApp* pApp );

static void WB_UpdatePenPosition( CWhiteBoardApp* pApp, EPenDirectionType eDirection, uint16 nStepSize );
static void WB_UpdateVertexDisplay( void* pData );
static void WB_CommitShape( CWhiteBoardApp* pApp );
static void WB_SetRGBColor( CWhiteBoardApp* pApp );
static int16 WB_CalculateAngle( AEEPoint* pOrigin, AEEPoint* pRadius );

static void WB_UpdateScrollBar( CWhiteBoardApp* pApp );
static void WB_UpdateTitleBar( CWhiteBoardApp* pApp, uint16 nTitleID );
static void WB_InitScreen( CWhiteBoardApp* pApp );
static void WB_ReleaseObj(void ** ppObj);


//*****************************************************************************
// APPLICATION-WIDE INITIALIZATION/CLEANUP FUNCTIONS
//****************************************************************************

/*===========================================================================

FUNCTION: AEEClsCreateInstance

DESCRIPTION:
	This function is invoked while the app is being loaded. All Modules must provide this 
	function. Ensure to retain the same name and parameters for this function.
	In here, the module must verify the ClassID and then invoke the AEEApplet_New() function
	that has been provided in AEEAppGen.c. 

  After invoking AEEApplet_New(), this function can do app specific initialization. In this
  example, a generic structure is provided so that app developers need not change app specific
  initialization section every time except for a call to WB_InitAppData(). 
  This is done as follows: WB_InitAppData() is called to initialize AppletData 
  instance. It is app developers responsibility to fill-in app data initialization 
  code of WB_InitAppData(). App developer is also responsible to release memory 
  allocated for data contained in AppletData -- this can be done in 
  WB_FreeAppData().

PARAMETERS:
	clsID [in] - Specifies the ClassID of the applet which is being loaded

	pIShell [in] - Contains pointer to the IShell interface. 

	pIModule [pin] - Contains pointer to the IModule interface to the current module to which
    this app belongs

	ppObj [out] - On return, *ppObj must point to a valid IApplet structure. Allocation
	  of memory for this structure and initializing the base data members is done by AEEApplet_New().

DEPENDENCIES:
  None

RETURN VALUE:
  AEE_SUCCESS - If the app needs to be loaded and if AEEApplet_New() invocation was
     successful
  EFAILED - If the app does not need to be loaded or if errors occurred in 
     AEEApplet_New(). If this function returns FALSE, the app will not be loaded.

SIDE EFFECTS:
  None
===========================================================================*/

int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj)
{
  CWhiteBoardApp* pApp = NULL; 

  if(!AEEApplet_New( sizeof( CWhiteBoardApp ), ClsId, pIShell, po,(IApplet**) ppObj, (AEEHANDLER) WB_HandleEvent, (PFNFREEAPPDATA) WB_FreeAppData ) )
    return ENOMEMORY;
	
  pApp = ( CWhiteBoardApp*) *ppObj;

  if (!WB_InitAppData( pApp ) ) 
  {
	  return EFAILED;
  }

   return( AEE_SUCCESS );
}

/*===========================================================================

FUNCTION: WB_InitAppData

DESCRIPTION:
  This function initializes app specific data, allocates memory for app data 
  (AppletData) and sets it to pAppData of AEEApplet. It also functions to 
  create all necessary GUI objects and initialize data in the applet's 
  global data structure.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes pi is not NULL

RETURN VALUE:
  TRUE: If the app has app data is allocated and initialized successfully
  FALSE: Either app data could not be allocated or initialzied

SIDE EFFECTS:
  None
===========================================================================*/

static boolean WB_InitAppData( CWhiteBoardApp* pApp )
{
  AEEDeviceInfo* pDeviceInfo;
  AEEImageInfo rImageInfo;
  uint16 nScreenWidth;
  uint16 nScreenHeight;
  AEERect rRect;
  AEEMenuColors rMenuColors;
  IBitmap* pDeviceBitmap;
  AEEItemStyle rNormalStyle;
  AEEItemStyle rSelStyle;

  // Create each of the controls used by the application.

  if( ( ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_GRAPHICS, (void**)( &pApp->m_pGraphics ) ) != SUCCESS ) ||
      ( ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_SOFTKEYCTL, (void**)( &pApp->m_pSoftKeyCtl ) ) != SUCCESS ) ||
      ( ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_ICONVIEWCTL, (void**)( &pApp->m_pIconViewCtl ) ) != SUCCESS ) ||
      ( ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_STATIC, (void**)( &pApp->m_pMainTextCtl ) ) != SUCCESS ) ||
      ( ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_LISTCTL, (void**)( &pApp->m_pModeCtl ) ) != SUCCESS ) )
    return FALSE;

  // Load the title bar image
  pApp->m_pTitleBarImage = ISHELL_LoadResImage( pApp->a.m_pIShell, WHITEBOARD_RES_FILE, IDB_TITLEBAR );

  // Make the image transparent

  IIMAGE_SetParm( pApp->m_pTitleBarImage, IPARM_ROP, AEE_RO_TRANSPARENT, 0 );

  // Set the style of the icon view control so that all the icons are displayed as
  // transparent.  This is set based on the default menu styles.

  ISHELL_GetItemStyle( pApp->a.m_pIShell, AEE_IT_MENU, &rNormalStyle, &rSelStyle );
  rNormalStyle.roImage = AEE_RO_TRANSPARENT;
  rSelStyle.roImage = AEE_RO_TRANSPARENT;
  IMENUCTL_SetStyle( pApp->m_pIconViewCtl, &rNormalStyle, &rSelStyle );

  // Get device information, fonr sizes and other measurements and store the necessary
  // values.  This is used to determine placement of objects on the device screen.
  pDeviceInfo = (AEEDeviceInfo*) MALLOC( sizeof( AEEDeviceInfo ) );
  if( !pDeviceInfo )
    return FALSE;

  ISHELL_GetDeviceInfo( pApp->a.m_pIShell, pDeviceInfo );
  pApp->m_nScreenColorDepth = pDeviceInfo->nColorDepth;
  nScreenWidth = pDeviceInfo->cxScreen;
  nScreenHeight = pDeviceInfo->cyScreen;
  FREE( pDeviceInfo );

  // If the device screen is 1, 2, or 4 bits, we use a single control to indicate 
  // the colors that can be selected.  This is because of the relatively small
  // number of colors each of these screens can support.  If the color depth is 
  // 8 bits or greater, then create 3 RGBCtl controls to allow the user to 
  // specify colors using true RGB values.

  if( pApp->m_nScreenColorDepth >= 8 )
  {
    pApp->m_pRGBRedCtl = CRGBCtl_CreateInstance();
    pApp->m_pRGBGreenCtl = CRGBCtl_CreateInstance();
    pApp->m_pRGBBlueCtl = CRGBCtl_CreateInstance();

    if( !pApp->m_pRGBRedCtl || !pApp->m_pRGBGreenCtl || !pApp->m_pRGBBlueCtl )
      return FALSE;
  }
  else
  {
    if( ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_LISTCTL, (void**)( &pApp->m_pColorCtl ) ) != SUCCESS )
      return FALSE;
  }

  // Set the menu control colors used fo the mode and color menu controls

  rMenuColors.wMask = 0x000F;
  rMenuColors.cBack = RGB_WHITE;
  rMenuColors.cText = RGB_BLACK;
  rMenuColors.cSelBack = RGB_BLACK;
  rMenuColors.cSelText = RGB_WHITE;

  if( pApp->m_nScreenColorDepth < 8 )
    IMENUCTL_SetColors( pApp->m_pColorCtl, &rMenuColors );
  IMENUCTL_SetColors( pApp->m_pModeCtl, &rMenuColors );

  // Get device specific information that will be used throughout the application
  // to facilitate placement of objects on the device screen.

  if( pApp->m_pTitleBarImage )
    IIMAGE_GetInfo( pApp->m_pTitleBarImage, &rImageInfo );
  else
    rImageInfo.cy = 0;

  pApp->m_nFontHeight = IDISPLAY_GetFontMetrics( pApp->a.m_pIDisplay, AEE_FONT_NORMAL, NULL, NULL ) + 1;

  SETAEERECT( &pApp->m_rTitleBarRect, 0, rImageInfo.cy, nScreenWidth, pApp->m_nFontHeight );
  SETAEERECT( &pApp->m_rScreenRect, 0, pApp->m_rTitleBarRect.y + pApp->m_rTitleBarRect.dy, nScreenWidth, nScreenHeight - ( rImageInfo.cy + pApp->m_rTitleBarRect.dy ) );
  SETAEERECT( &pApp->m_rHScrollBarRect, 0, ( ( pApp->m_rScreenRect.y + pApp->m_rScreenRect.dy ) - MAIN_MENU_HEIGHT ) - ( SCROLLBAR_HEIGHT ), pApp->m_rScreenRect.dx, SCROLLBAR_HEIGHT );
  SETAEERECT( &pApp->m_rViewportRect, 0, pApp->m_rTitleBarRect.y + pApp->m_rTitleBarRect.dy, pApp->m_rScreenRect.dx, pApp->m_rHScrollBarRect.y - ( pApp->m_rTitleBarRect.y + pApp->m_rTitleBarRect.dy ) );

  // Create the bitmap used to double buffer the drawing surface.  To do this,
  // we get the device bitmap from the IDiplay object, and then create a new
  // one using the display bitmap as a template.
  // Set the size of the Bitmap to double buffer the graphics drawn to the screen.
  // For now we will make the Bitmap dimensions the same height as the viewport
  // and twice the width.

  if( IDISPLAY_GetDeviceBitmap( pApp->a.m_pIDisplay, &pDeviceBitmap ) != SUCCESS )
    return FALSE;

  pApp->m_nBitmapWidth = pApp->m_rViewportRect.dx * 2;

  if( IBITMAP_CreateCompatibleBitmap( pDeviceBitmap, &pApp->m_pBitmap, pApp->m_nBitmapWidth, pApp->m_rViewportRect.dy ) != SUCCESS )
  {
    IBITMAP_Release( pDeviceBitmap );
    return FALSE;
  }
  IBITMAP_Release( pDeviceBitmap );
  SETAEERECT( &rRect, 0, 0, pApp->m_nBitmapWidth, pApp->m_rViewportRect.dy );
  IBITMAP_FillRect( pApp->m_pBitmap, &rRect, IBITMAP_RGBToNative( pApp->m_pBitmap, RGB_WHITE ), AEE_RO_COPY );
  pApp->m_nWindowXOrigin = 0;

  // Set the initial shape and fill flag as well as the default border and fill
  // colors.

  pApp->m_nShapeType = IDC_LINE;
  pApp->m_bFillShape = FALSE;
  pApp->m_nBorderColor = 0;
  pApp->m_nFillColor = 0;
  pApp->m_nPenStepSize = 1;

  // Set the initial image of the Draw button of the Main Menu.

  pApp->m_nDrawIconID = IDB_LINE;

  // Initialize the current state of the state machine.

  pApp->m_eAppState = APP_STATE_START;
  
  // Initialize shape information array.  This array indicates the
  // number of points required to specify each shape type.  Shapes
  // that have a value of -1 indicate that a variable number of 
  // points can be specified up to the maximum value defined in 
  // MAX_NUM_POINTS.

  pApp->m_pShapeInfo[ IDC_LINE ] =      NUM_POINTS_2;
  pApp->m_pShapeInfo[ IDC_POLYLINE ] =  NUM_POINTS_VARIABLE;
  pApp->m_pShapeInfo[ IDC_ARC ] =       NUM_POINTS_3;
  pApp->m_pShapeInfo[ IDC_CIRCLE ] =    NUM_POINTS_2; 
  pApp->m_pShapeInfo[ IDC_ELLIPSE ] =   NUM_POINTS_2;
  pApp->m_pShapeInfo[ IDC_PIE ] =       NUM_POINTS_3;
  pApp->m_pShapeInfo[ IDC_RECTANGLE ] = NUM_POINTS_2;
  pApp->m_pShapeInfo[ IDC_TRIANGLE ] =  NUM_POINTS_3;
  pApp->m_pShapeInfo[ IDC_POLYGON ] =   NUM_POINTS_VARIABLE;
  pApp->m_pShapeInfo[ IDC_CLIP_RECT ] = NUM_POINTS_2;

  return TRUE;
}

/*===========================================================================

FUNCTION: WB_FreeAppData

DESCRIPTION:
  Frees data contained in app data and memory for app data itself.
  App developer needs to free data contained in AppletData.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
   Assumes pi is not NULL

RETURN VALUE:
   None

SIDE EFFECTS:
   None
===========================================================================*/

static void WB_FreeAppData( CWhiteBoardApp* pApp )
{
  WB_ReleaseObj( (void**) &pApp->m_pTitleBarImage );
  WB_ReleaseObj( (void**) &pApp->m_pIconViewCtl );
  WB_ReleaseObj( (void**) &pApp->m_pSoftKeyCtl );
  WB_ReleaseObj( (void**) &pApp->m_pModeCtl );
  WB_ReleaseObj( (void**) &pApp->m_pMainTextCtl );
  WB_ReleaseObj( (void**) &pApp->m_pGraphics );
  WB_ReleaseObj( (void**) &pApp->m_pBitmap );
  WB_ReleaseObj( (void**) &pApp->m_pSuspendBitmap );

  if( pApp->m_nScreenColorDepth >= 8 )
  {
    CRGBCtl_Release( pApp->m_pRGBRedCtl );
    CRGBCtl_Release( pApp->m_pRGBGreenCtl );
    CRGBCtl_Release( pApp->m_pRGBBlueCtl );
  }
  else
    WB_ReleaseObj( (void**) &pApp->m_pColorCtl );
}

//*****************************************************************************
// EVENT HANDLING FUNCTIONS
//*****************************************************************************

/*===========================================================================

FUNCTION: WB_HandleEvent

DESCRIPTION:
	This is the main EventHandler for this app. All events to this app are handled
	by this function. All applications must supply an Event Handler. 

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

	eCode [in] - Specifies the Event sent to this applet

  wParam [in] - 16-bit event specific data.

  dwParam [in] - 32-bit event specific data.

DEPENDENCIES:
  None

RETURN VALUE:
  TRUE - If the app has processed the event
  FALSE - If the app did not process the event

SIDE EFFECTS:
  None
===========================================================================*/

static boolean WB_HandleEvent( CWhiteBoardApp* pApp, AEEEvent eCode, uint16 wParam, uint32 dwParam )
{
  CtlAddItem rCtlAddItem;
  IBitmap* pDeviceBitmap;

  // Handle any events that need to be handled by the application.

  switch( eCode )
  {
  case EVT_APP_START:  // App started...display the splash screen
    WB_DisplaySplashScreen( pApp );
    return TRUE;

  case EVT_APP_STOP:  // Acknowledge an app stop event
    return TRUE;

  case EVT_APP_SUSPEND:  // Prepare the application to suspend
    // Create a bitmap that captures the current state of the screen
    if( IDISPLAY_GetDeviceBitmap( pApp->a.m_pIDisplay, &pDeviceBitmap ) != SUCCESS )
      return FALSE;
    if( IBITMAP_CreateCompatibleBitmap( pDeviceBitmap, &pApp->m_pSuspendBitmap, (uint16) ( pApp->m_rScreenRect.dx + pApp->m_rScreenRect.x ), (uint16) ( pApp->m_rScreenRect.dy + pApp->m_rScreenRect.y ) ) != SUCCESS )
    {
      IBITMAP_Release( pDeviceBitmap );
      return FALSE;
    }
    IBITMAP_BltIn( pApp->m_pSuspendBitmap, 0, 0, (uint16) ( pApp->m_rScreenRect.dx + pApp->m_rScreenRect.x ), (uint16) ( pApp->m_rScreenRect.dy + pApp->m_rScreenRect.y ), pDeviceBitmap, 0, 0, AEE_RO_COPY );
    IBITMAP_Release( pDeviceBitmap );
    return TRUE;

  case EVT_APP_RESUME:  // Prepare tor resume the app
    // Copy the capture bitmap to the screen to restroe the display
    IDISPLAY_BitBlt( pApp->a.m_pIDisplay, 0, 0, pApp->m_rScreenRect.dx + pApp->m_rScreenRect.x, pApp->m_rScreenRect.dy + pApp->m_rScreenRect.y, pApp->m_pSuspendBitmap, 0, 0, AEE_RO_COPY );
    IDISPLAY_Update( pApp->a.m_pIDisplay );
    WB_ReleaseObj( (void**) &pApp->m_pSuspendBitmap );
    return TRUE;

  case EVT_COPYRIGHT_END:  // The about dialog is done displaying so display the Main Menu screen
    WB_DisplayMainScreen( pApp );
    return TRUE;

  case EVT_KEY:  // Handle necessary key events
    switch( wParam )
    {
    case AVK_CLR:  // Handle actions when the user presses the clear key
      switch( pApp->m_eAppState )
      {
      case APP_STATE_DRAW:
        // Handle Clr key presses in the Draw state
        if( pApp->m_nNumPoints > 0 )
        {
          // Cancel pixel blink timer and clear out the point array
          pApp->m_nNumPoints = 0;
          ISHELL_CancelTimer( pApp->a.m_pIShell, (PFNNOTIFY) WB_UpdateVertexDisplay, pApp );
          IDISPLAY_BitBlt( pApp->a.m_pIDisplay, pApp->m_rViewportRect.x, pApp->m_rViewportRect.y, pApp->m_rViewportRect.dx, pApp->m_rViewportRect.dy, pApp->m_pBitmap, pApp->m_nWindowXOrigin, 0, AEE_RO_COPY );
          IDISPLAY_Update( pApp->a.m_pIDisplay );
          WB_UpdatePenPosition( pApp, PEN_SHOW, 0 );
          // If we are in the Clipping state, transition to the Draw state
          if( pApp->m_nShapeType == IDC_CLIP_RECT )
          {
            WB_UpdateTitleBar( pApp, IDS_DRAW_MODE );
            pApp->m_nShapeType = pApp->m_nShapeTypeBeforeClip;
          }
        }
        else
        {
          // We are in the draw state so transition to the Main Menu state
          WB_DisplayMainScreen( pApp );
        }
        return TRUE;

      // For all remaining Clr events, transition to the previous screen base on the
      // state we are currently in.
      case APP_STATE_LINES_MENU:
      case APP_STATE_FILLED_SHAPES_MENU:
      case APP_STATE_UNFILLED_SHAPES_MENU:
        WB_DisplayShapeMenu( pApp );
        return TRUE;

      case APP_STATE_COLORS_MENU:
      case APP_STATE_SHAPES_MENU:
        WB_DisplayOptionMenu( pApp );
        return TRUE;

      case APP_STATE_OPTIONS_MENU:
        WB_DisplayMainScreen( pApp );
        return TRUE;

      case APP_STATE_HELP:
      case APP_STATE_ABOUT:
        WB_DisplayMainScreen( pApp );
        return TRUE;
      }
      break;

    case AVK_LEFT:
      switch( pApp->m_eAppState )
      {
      case APP_STATE_DRAW:
        // Handle the left button in the draw state
        WB_UpdatePenPosition( pApp, PEN_LEFT, pApp->m_nPenStepSize );
        return TRUE;
      }
      break;

    case AVK_RIGHT:
      switch( pApp->m_eAppState )
      {
      case APP_STATE_DRAW:
        // Handle the right button in the draw state
        WB_UpdatePenPosition( pApp, PEN_RIGHT, pApp->m_nPenStepSize );
        return TRUE;
      }
      break;

    case AVK_UP:
      switch( pApp->m_eAppState )
      {
      case APP_STATE_DRAW:
        // Handle the up button in the draw state
        WB_UpdatePenPosition( pApp, PEN_UP, pApp->m_nPenStepSize );
        return TRUE;
      }
      break;

    case AVK_DOWN:
      switch( pApp->m_eAppState )
      {
      case APP_STATE_DRAW:
        // Handle the down button in the draw state
        WB_UpdatePenPosition( pApp, PEN_DOWN, pApp->m_nPenStepSize );
        return TRUE;
      }
      break;

    case AVK_1:  // If a numeric key is pressed, set the number of pixels the pen should
    case AVK_2:  // move each time one of the direction keys is pressed.
    case AVK_3:
    case AVK_4:
    case AVK_5:
    case AVK_6:
    case AVK_7:
    case AVK_8:
    case AVK_9:
      if( pApp->m_eAppState == APP_STATE_DRAW )
      {
        pApp->m_nPenStepSize = wParam - AVK_0;
        return TRUE;
      }
      break;

    case AVK_SELECT:  // Handle select key events when we are in drawing mode.
      if( WB_HandleAVKSelectKeyEvent( pApp ) )
        return TRUE;
    }
    break;
      
  // This event indicates that the user has changed the selection in an IMENUCTL control.
  // This is used for the mode and color menu controls on the color screen of the application.

  case EVT_CTL_SEL_CHANGED:  // Handle menu selection changes

    // Update the color control values when the mode menu control selection is changed.

    if( (IMenuCtl*) dwParam == pApp->m_pModeCtl )
    {
      if( pApp->m_nScreenColorDepth >= 8 )
      {
        CRGBCtl_SetMode( pApp->m_pRGBRedCtl, (boolean) ( wParam - IDC_FILL ) );
        CRGBCtl_SetMode( pApp->m_pRGBGreenCtl, (boolean) ( wParam - IDC_FILL ) );
        CRGBCtl_SetMode( pApp->m_pRGBBlueCtl, (boolean) ( wParam - IDC_FILL ) );
      }
      else
      {
        if( wParam == IDC_BORDER )
          IMENUCTL_SetSel( pApp->m_pColorCtl, pApp->m_nBorderColor );
        else
          IMENUCTL_SetSel( pApp->m_pColorCtl, pApp->m_nFillColor );
      }
      return TRUE;
    }

    // Update the sample shape image when a color control is changed.

    if( (IMenuCtl*) dwParam == pApp->m_pColorCtl )
    {
      if( IMENUCTL_GetSel( pApp->m_pModeCtl ) == IDC_BORDER )
        pApp->m_nBorderColor = (unsigned char) wParam;
      else
        pApp->m_nFillColor = (unsigned char) wParam;
      WB_DisplaySampleShape( pApp );
      return TRUE;
    }

    if( (CRGBCtl*) dwParam == pApp->m_pRGBRedCtl ||
        (CRGBCtl*) dwParam == pApp->m_pRGBGreenCtl ||
        (CRGBCtl*) dwParam == pApp->m_pRGBBlueCtl ||
        (IMenuCtl*) dwParam == pApp->m_pColorCtl )
    {
      WB_DisplaySampleShape( pApp );
      return TRUE;
    }
    break;

  // This event is sent by each control when the user presses a key that tells 
  // control that focus is being shifted to another control.  By handling this
  // event, the user can move from one control to the next.

  case EVT_CTL_TAB:  
    if( pApp->m_eAppState == APP_STATE_COLORS_MENU )
    {
      if( pApp->m_nScreenColorDepth >= 8 )
      {
        // Handle tab events for 8, 16 and 24-bit color screens
        if( wParam )
        {
          // Handle right tab events
          if( CRGBCtl_IsActive( pApp->m_pRGBRedCtl ) )
          {
            CRGBCtl_SetActive( pApp->m_pRGBRedCtl, FALSE );
            CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, TRUE );
            return TRUE;
          }
          if( CRGBCtl_IsActive( pApp->m_pRGBGreenCtl ) )
          {
            CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, FALSE );
            if( pApp->m_bFillShape )
              IMENUCTL_SetActive( pApp->m_pModeCtl, TRUE );
            else
              CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pModeCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pModeCtl, FALSE );
            CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, TRUE );
            return TRUE;
          }
          if( CRGBCtl_IsActive( pApp->m_pRGBBlueCtl ) )
          {
            CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, FALSE );
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pSoftKeyCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, FALSE );
            CRGBCtl_SetActive( pApp->m_pRGBRedCtl, TRUE );
          }
        }
        else
        {
          // Handle left tab events
          if( CRGBCtl_IsActive( pApp->m_pRGBRedCtl ) )
          {
            CRGBCtl_SetActive( pApp->m_pRGBRedCtl, FALSE );
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, TRUE );
            return TRUE;
          }
          if( CRGBCtl_IsActive( pApp->m_pRGBGreenCtl ) )
          {
            CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, FALSE );
            CRGBCtl_SetActive( pApp->m_pRGBRedCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pModeCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pModeCtl, FALSE );
            CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, TRUE );
            return TRUE;
          }
          if( CRGBCtl_IsActive( pApp->m_pRGBBlueCtl ) )
          {
            CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, FALSE );
            if( pApp->m_bFillShape )
              IMENUCTL_SetActive( pApp->m_pModeCtl, TRUE );
            else
              CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pSoftKeyCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, FALSE );
            CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, TRUE );
          }
        }
      }
      else
      {
        // Handle tab events for 1,2 and 4-bit color screens
        if( wParam )
        {
          // Handle right tab events
          if( IMENUCTL_IsActive( pApp->m_pColorCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pColorCtl, FALSE );
            if( pApp->m_bFillShape )
              IMENUCTL_SetActive( pApp->m_pModeCtl, TRUE );
            else
              IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pModeCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pModeCtl, FALSE );
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pSoftKeyCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, FALSE );
            IMENUCTL_SetActive( pApp->m_pColorCtl, TRUE );
          }
        }
        else
        {
          // Handle left tab events
          if( IMENUCTL_IsActive( pApp->m_pColorCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pColorCtl, FALSE );
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pModeCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pModeCtl, FALSE );
            IMENUCTL_SetActive( pApp->m_pColorCtl, TRUE );
            return TRUE;
          }
          if( IMENUCTL_IsActive( pApp->m_pSoftKeyCtl ) )
          {
            IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, FALSE );
            if( pApp->m_bFillShape )
              IMENUCTL_SetActive( pApp->m_pModeCtl, TRUE );
            else
              IMENUCTL_SetActive( pApp->m_pColorCtl, TRUE );
          }
        }
      }
    }
    break;

  case EVT_COMMAND:  // Handle commands sent from various controls
    switch( wParam )
    {
    case IDC_DRAW:  // The draw button on the Main Menu was pressed so go into darw mode
      WB_DisplayDrawScreen( pApp );
      return TRUE;

    case IDC_TOOLS: // The tools button on the Main Menu was pressed so show the tools screen
      WB_DisplayOptionMenu( pApp );
      return TRUE;

    case IDC_HELP:  // The help button on the Main Menu was pressed so show the help screen
      WB_DisplayInfoScreen( pApp, IDC_HELP, (uint32) WHITEBOARD_HELP_FILE, TRUE );
      pApp->m_eAppState = APP_STATE_HELP;
      return TRUE;

    case IDC_ABOUT:  // The About button on the Main Menu was pressed so show the about screen
      WB_InitScreen( pApp );
      IDISPLAY_ClearScreen( pApp->a.m_pIDisplay );
      ISHELL_ShowCopyright( pApp->a.m_pIShell );
      pApp->m_eAppState = APP_STATE_ABOUT;
      return TRUE;

    case IDC_SHAPES:  // The shapes tool was pressed so show the shapes screen
      WB_DisplayShapeMenu( pApp );
      return TRUE;

    case IDC_COLOR:  // The color tool was pressed so show the color screen
      WB_DisplayColorScreen( pApp );
      return TRUE;
    
    case IDC_LINES:  // The line shape was pressed so show the lines screen
      WB_DisplayLineMenu( pApp );
      return TRUE;
    
    case IDC_FILLED_SHAPES:  // The filled shapes button was pressed so show the filled shapes screen
      WB_DisplayFilledShapeMenu( pApp );
      return TRUE;

    case IDC_UNFILLED_SHAPES:  // The unfilled shapes button was pressed so show the unfilled shapes screen
      WB_DisplayUnfilledShapeMenu( pApp );
      return TRUE;

    case IDC_LINE:     // A line type was selected so set it as the current shape
    case IDC_POLYLINE:
    case IDC_ARC:
      pApp->m_nShapeType = wParam;
      pApp->m_bFillShape = FALSE;
      if( IMENUCTL_GetItem( pApp->m_pIconViewCtl, wParam, &rCtlAddItem ) )
        pApp->m_nDrawIconID = rCtlAddItem.wImage;
      WB_DisplayOptionMenu( pApp );
      return TRUE;

    case IDC_CIRCLE:   // A shape type was selected so set the shape and fill type
    case IDC_ELLIPSE:
    case IDC_PIE:
    case IDC_RECTANGLE:
    case IDC_TRIANGLE:
    case IDC_POLYGON:
      pApp->m_nShapeType = wParam;
      if( pApp->m_eAppState == APP_STATE_FILLED_SHAPES_MENU )
        pApp->m_bFillShape = TRUE;
      else
        pApp->m_bFillShape = FALSE;
      if( IMENUCTL_GetItem( pApp->m_pIconViewCtl, wParam, &rCtlAddItem ) )
        pApp->m_nDrawIconID = rCtlAddItem.wImage;
      WB_DisplayOptionMenu( pApp );
      return TRUE;

    case IDC_OK:   // The OK or done button was pressed on a screen so return to the Main Screen
    case IDC_DONE:
      WB_DisplayMainScreen( pApp );
      return TRUE;
    }
    break;
  }
  // The application did not need to handle the event so pass it to each
  // of the controls to let then deal with it.

  if( IMENUCTL_HandleEvent( pApp->m_pIconViewCtl, eCode, wParam, dwParam ) ) 
    return TRUE;

  if( ISTATIC_HandleEvent( pApp->m_pMainTextCtl, eCode, wParam, dwParam ) )
    return TRUE;

  if( pApp->m_nScreenColorDepth >= 8 )
  {
    if( CRGBCtl_HandleEvent( pApp->m_pRGBRedCtl, eCode, wParam, dwParam ) )
      return TRUE;

    if( CRGBCtl_HandleEvent( pApp->m_pRGBGreenCtl, eCode, wParam, dwParam ) )
      return TRUE;

    if( CRGBCtl_HandleEvent( pApp->m_pRGBBlueCtl, eCode, wParam, dwParam ) )
      return TRUE;
  }
  else
  {
    if( IMENUCTL_HandleEvent( pApp->m_pColorCtl, eCode, wParam, dwParam ) )
      return TRUE;
  }
  if( IMENUCTL_HandleEvent( pApp->m_pModeCtl, eCode, wParam, dwParam ) )
    return TRUE;

  if( IMENUCTL_HandleEvent( pApp->m_pSoftKeyCtl, eCode, wParam, dwParam ) )
    return TRUE;

  return FALSE;
}

/*===========================================================================

FUNCTION: WB_HandleAVKSelectKeyEvent

DESCRIPTION:
	This function performs the necessary event-handling when an AVK_SELECT
  event is detected in the WB_HandleEvent function.  This functionality
  could easily have been located in WB_HandleEvent but has been moved to
  a seperate function due to the amount of processing involved.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  None

RETURN VALUE:
  Returns TRUE if the AVK_SELECT event was handled.  Otherwise, returns
  FALSE.

SIDE EFFECTS:
  May cause the phone display to be updated.

===========================================================================*/

static boolean WB_HandleAVKSelectKeyEvent( CWhiteBoardApp* pApp )
{
  if( pApp->m_eAppState == APP_STATE_DRAW )
  {
    // Select key is pressed while the pen is on the drawing canvas so handle this event.

    if( pApp->m_nNumPoints == 0 )
    {
      // This is the first point specified so start the Vertex Blink timer
      // via a call to WB_UpdateVertexDisplay.

      pApp->m_bBlinkMode = TRUE;
      WB_UpdateVertexDisplay( (void*) pApp );
    }

    // Handle the case where at least on point has been specified on the drawing screen
    // and the user pressed the select key twice in the same location

    if( pApp->m_nNumPoints > 0 && ( pApp->m_rPenLoc.x == pApp->m_pPointList[ pApp->m_nNumPoints - 1 ].x && pApp->m_rPenLoc.y == pApp->m_pPointList[ pApp->m_nNumPoints - 1 ].y ) )
    {
      // If the select key is pressed twice in the same location on the first specified
      // point then initiaite clipping mode.

      if( pApp->m_nNumPoints == 1 )
      {
        // Set title bar to indicate clipping mode is enguaged

        WB_UpdateTitleBar( pApp, IDS_CLIP_MODE );

        // Save current shape (this value will be restored later)

        pApp->m_nShapeTypeBeforeClip = pApp->m_nShapeType;

        // Set the current shape to the clip shape

        pApp->m_nShapeType = IDC_CLIP_RECT;
        return TRUE;
      }

      // If the current shape type allows a variable number of pixels to be specified (i.e. polyline)
      // the drawing operation is complete to commit the shape to the canvas.

      if( pApp->m_pShapeInfo[ pApp->m_nShapeType ] == -1 )
      {
        // Render the shape to the drawing canvas

        WB_CommitShape( pApp );

        // The drawing operation is complete so reset the point array

        pApp->m_nNumPoints = 0;
        return TRUE;
      }
    }
    // Add the currently selected point to the point array

    pApp->m_pPointList[ pApp->m_nNumPoints ].x = pApp->m_rPenLoc.x;
    pApp->m_pPointList[ pApp->m_nNumPoints ].y = pApp->m_rPenLoc.y;
    pApp->m_nNumPoints++;

    // Render the updated point array to the screen

    WB_UpdateVertexDisplay( (void*) pApp );

    if( pApp->m_nNumPoints == pApp->m_pShapeInfo[ pApp->m_nShapeType ] || pApp->m_nNumPoints == MAX_NUM_POINTS )
    {
      // Render the shape to the drawing canvas
  
      WB_CommitShape( pApp );

      // The drawing operation is complete so reset the point array

      pApp->m_nNumPoints = 0;
    }
    return TRUE;
  }
  return FALSE;
}

//*****************************************************************************
// DISPLAY MANIPULATION FUNCTIONS
//****************************************************************************

/*===========================================================================

FUNCTION: WB_DisplaySplashScreen

DESCRIPTION:
	This function clears the device screen and displays the application's 
  splash screen in the center of the display.  A timer is initiated
  which, upon expiration, calls the WB_DisplayMainScreen function to clear 
  the screen and start the application's Main Menu.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.

===========================================================================*/

static void WB_DisplaySplashScreen( CWhiteBoardApp* pApp )
{
  IImage* pSplash;
  AEEImageInfo rImageInfo;

  // The current app state is APP_STATE_START, clear the screen, load the resource image
  // and display it.

  IDISPLAY_ClearScreen( pApp->a.m_pIDisplay );
  pSplash = ISHELL_LoadResImage( pApp->a.m_pIShell, WHITEBOARD_RES_FILE, IDB_SPLASH );

  if( pSplash )
  {
    // Get image information

    IIMAGE_GetInfo( pSplash, &rImageInfo );

    // Draw the image in the center of the screen

    IIMAGE_Draw( pSplash, ( pApp->m_rScreenRect.dx / 2 ) - ( rImageInfo.cx / 2 ), ( ( pApp->m_rScreenRect.dy + pApp->m_rScreenRect.y ) / 2 ) - ( rImageInfo.cy / 2 ) );

    // The image is no longer needed so release it

    IIMAGE_Release( pSplash );

    // Set the callback timer to call this function again

    ISHELL_SetTimer( pApp->a.m_pIShell, SPLASH_TIMER_DURATION, ( PFNNOTIFY ) WB_DisplayMainScreen, pApp );
  }
  else
  {
    WB_DisplayMainScreen( pApp );
  }
  IDISPLAY_Update( pApp->a.m_pIDisplay );
}

/*===========================================================================

FUNCTION: WB_DisplayMainScreen

DESCRIPTION:
	This function clears the device screen and displays the components of
  the application's Main Menu screen.  This consists of updating the 
  title bar text at the top of the screen, the Menu Control at the 
  bottom of the screen, and the viewport of the drawing canvas/scrollbar in
  the center.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_DisplayMainScreen( CWhiteBoardApp* pApp )
{
  WB_InitScreen( pApp );
  WB_UpdateScrollBar( pApp );
  WB_DisplayMainMenu( pApp );

  // Update the canvas image to the viewport

  IDISPLAY_BitBlt( pApp->a.m_pIDisplay, pApp->m_rViewportRect.x, pApp->m_rViewportRect.y, pApp->m_rViewportRect.dx, pApp->m_rViewportRect.dy, pApp->m_pBitmap, pApp->m_nWindowXOrigin, 0, AEE_RO_COPY );
  IDISPLAY_Update( pApp->a.m_pIDisplay );
  pApp->m_eAppState = APP_STATE_MAIN_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayColorScreen

DESCRIPTION:
	This function clears the device screen and displays the components of
  the application's Color screen.  This consists of updating the 
  title bar text at the top of the screen, initializing the menu items
  for each of the color selection controls, initializing the menu items
  of the Soft Key menu control (at the bottom of the screen) and displaying
  a sample shape (of the correct color and fill values) in the upper
  1/3 of the screen.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the applcation to the APP_STATE_COLORS_MENU state.
===========================================================================*/

static void WB_DisplayColorScreen( CWhiteBoardApp* pApp )
{
  uint16 nHalfScreenWidth = pApp->m_rScreenRect.dx / 2;
  uint16 nCtlYPos;
  AEERect rRect;
  AECHAR psTextBuf[ 4 ];
  AECHAR psFormat[] = { '%','i','\0' };
  uint8 i;

  // If the target device has a 1, 2 or 4 bit screen, initialize the menu items
  // in the color control (only once).  We do this by checking if the control 
  // currently has any menu items.  This initialization need not be done for 
  // 8-bit or greater screens since these screens utilize the CRGBCtl controls
  // instead.

  if( pApp->m_nScreenColorDepth < 8 )
  {
    if( IMENUCTL_GetItemCount( pApp->m_pColorCtl ) == 0 )
    {
      switch( pApp->m_nScreenColorDepth )
      {
      case 1:
        IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, IDC_BLACK, 0, 0, 0 );
        IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, IDC_WHITE, 1, 0, 1 );
        break;

      case 2:
        IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, IDC_BLACK, 0, 0, 0 );
        IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, IDC_GRAY1, 1, 0, 1 );
        IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, IDC_GRAY2, 2, 0, 2 );
        IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, IDC_WHITE, 3, 0, 3 );
        break;

      case 4:
        for( i = 0; i < 16; i++ )
        {
          WSPRINTF( psTextBuf, 4 * sizeof( AECHAR ), psFormat, i );
          IMENUCTL_AddItem( pApp->m_pColorCtl, WHITEBOARD_RES_FILE, 0, (uint16) i, psTextBuf, i );
        }
        break;
      }
    }
  }

  // Clear the device screen, render the application title image at the 
  // top of the screen and update the titlebar.

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, IDS_SELECT_COLORS );

  // Populate Soft Key menu items and draw it on the screen

  IMENUCTL_AddItem( pApp->m_pSoftKeyCtl, WHITEBOARD_RES_FILE, IDC_DONE, IDC_DONE, NULL, 0 );
  IMENUCTL_Redraw( pApp->m_pSoftKeyCtl );

  // Get the Y value where the lowest color controls are to be displayed.  This
  // is based on the top of the Soft Key menu control.

  IMENUCTL_GetRect( pApp->m_pSoftKeyCtl, &rRect );
  nCtlYPos = ( rRect.y - pApp->m_nFontHeight ) - 5;

  // Delete all items from the mode List control

  IMENUCTL_Reset( pApp->m_pModeCtl );

  // Populate the mode list items based on the current fill state

  IMENUCTL_AddItem( pApp->m_pModeCtl, WHITEBOARD_RES_FILE, IDC_BORDER, IDC_BORDER, NULL, 0 );
  if( pApp->m_bFillShape )
    IMENUCTL_AddItem( pApp->m_pModeCtl, WHITEBOARD_RES_FILE, IDC_FILL, IDC_FILL, NULL, 0 );
    
  // Place the mode List control on the screen if the shape is a filled shape.

  if( pApp->m_bFillShape )
    WB_DisplayControl( pApp, IDS_MODE, pApp->m_pModeCtl, 0, nCtlYPos, nHalfScreenWidth );

  if( pApp->m_nScreenColorDepth < 8 )
  {
    // If the device has a 1, 2 or 4 bit display, place the color List control on the screen

    nCtlYPos -= pApp->m_nFontHeight + 3;
    WB_DisplayControl( pApp, IDC_COLOR, (void*) pApp->m_pColorCtl, 0, nCtlYPos, nHalfScreenWidth );
    IMENUCTL_SetSel( pApp->m_pColorCtl, pApp->m_nBorderColor );
    IMENUCTL_SetActive( pApp->m_pColorCtl, TRUE );
  }
  else
  {
    // If the device has an 8 or 24 bit display, place the red, green and blue RGB controls on the screen

    nCtlYPos -= pApp->m_nFontHeight + 3;
    WB_DisplayControl( pApp, IDS_RED, (void*) pApp->m_pRGBRedCtl, 0, nCtlYPos, nHalfScreenWidth );
    CRGBCtl_SetMode( pApp->m_pRGBRedCtl, TRUE );
    CRGBCtl_SetActive( pApp->m_pRGBRedCtl, TRUE );

    nCtlYPos += pApp->m_nFontHeight + 3;
    WB_DisplayControl( pApp, IDS_BLUE, (void*) pApp->m_pRGBBlueCtl, (uint16) ( nHalfScreenWidth + 3 ), nCtlYPos, (uint16) ( nHalfScreenWidth - 3 ) );
    CRGBCtl_SetMode( pApp->m_pRGBBlueCtl, TRUE );
    CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, FALSE );

    nCtlYPos -= pApp->m_nFontHeight + 3;
    WB_DisplayControl( pApp, IDS_GREEN, (void*) pApp->m_pRGBGreenCtl, (uint16) ( nHalfScreenWidth + 3 ), nCtlYPos, (uint16) ( nHalfScreenWidth - 3 ) );
    CRGBCtl_SetMode( pApp->m_pRGBGreenCtl, TRUE );
    CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, FALSE );
  }

  // Display the sample shape on the screen

  WB_DisplaySampleShape( pApp );

  // Set the application state

  pApp->m_eAppState = APP_STATE_COLORS_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayInfoScreen

DESCRIPTION:
	This is a generic function used to display a text information screen.  A
  text information screen consists of the titlebar, static control in the
  cenetr of the screen and a soft key control at the bottom.

  This function is used to display the Help and About screens.  The text
  source displayed in the Static control can be either a wide string
  or a file.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

  nTitleID [in] - The resource ID of the titlebar text.

  nText [in] - The memory address of a file name (if the text source is from
    a file) or a wide string.  The type of object nText points to is determined
    by the value passed in bIsFile.

  bIsFile [in] - TRUE if the text source is a file.  FALSE if the text source 
    is a wide string.

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_DisplayInfoScreen( CWhiteBoardApp* pApp, uint16 nTitleID, uint32 nText, boolean bIsFile )
{
  AEERect rRect;
  IFileMgr* pFileMgr;
  IFile* pFile;
  AECHAR* psTextBuf;
  boolean bError = FALSE;

  // Clear the screen and update the titlebar

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, nTitleID );

  // Set the dimensions and placement of the IStatic control on the screen

  IMENUCTL_GetRect( pApp->m_pSoftKeyCtl, &rRect );
  SETAEERECT( &rRect, pApp->m_rScreenRect.x, pApp->m_rScreenRect.y, pApp->m_rScreenRect.dx, pApp->m_rScreenRect.dy - rRect.y ); 
  ISTATIC_SetRect( pApp->m_pMainTextCtl, &rRect );
  ISTATIC_SetProperties( pApp->m_pMainTextCtl, ST_NOSCROLL );

  // If bIsFile == TRUE, the value of nText is assumed to be a pointer to a file

  if( bIsFile )
  {
    // Create an IFileMgr object used to open the text file

    ISTATIC_SetProperties( pApp->m_pMainTextCtl, ST_ASCII );
    ISHELL_CreateInstance( pApp->a.m_pIShell, AEECLSID_FILEMGR, (void**)( &pFileMgr ) );
    if( pFileMgr )
    {

      // Open the text file and set the IStatic text to the file pointer

      pFile = IFILEMGR_OpenFile( pFileMgr, (char*) nText, _OFM_READ );
      IFILEMGR_Release( pFileMgr );

      if( pFile )
      {
        ISTATIC_SetTextEx( pApp->m_pMainTextCtl, NULL, (IAStream*) pFile, FALSE );
        IFILE_Release( pFile );
      }
      else
      {
        bError = TRUE;
      }
    }
    else
    {
      bError = TRUE;
    }
  }
  else
  {
    // bIsFile == FALSE so assume the value of nText points to a wide string.
    // Set the text of the IStatic control to the nText pointer

    ISTATIC_SetProperties( pApp->m_pMainTextCtl, ISTATIC_GetProperties( pApp->m_pMainTextCtl ) & (~ST_ASCII ) );
    ISTATIC_SetTextEx( pApp->m_pMainTextCtl, (byte*) nText, NULL, FALSE );
  }

  // If an error occured then display an error message

  if( bError )
  {
    psTextBuf = (AECHAR*) MALLOC( MAX_RES_STRING_BUF_SIZE * sizeof( AECHAR ) );
    if( psTextBuf )
    {
      ISTATIC_SetProperties( pApp->m_pMainTextCtl, ISTATIC_GetProperties( pApp->m_pMainTextCtl ) & (~ST_ASCII ) );
      ISHELL_LoadResString( pApp->a.m_pIShell, WHITEBOARD_RES_FILE, IDS_HELP_LOAD_ERROR, psTextBuf, MAX_RES_STRING_BUF_SIZE * sizeof( AECHAR ) );
      ISTATIC_SetText( pApp->m_pMainTextCtl, NULL, psTextBuf, AEE_FONT_NORMAL, AEE_FONT_NORMAL );
      FREE( psTextBuf );
    }
  }
  ISTATIC_Redraw( pApp->m_pMainTextCtl );

  // Populate Soft Key control with an OK button

  IMENUCTL_AddItem( pApp->m_pSoftKeyCtl, WHITEBOARD_RES_FILE, IDC_OK, IDC_OK, NULL, 0 );

  // Activate the Soft Key control and render it to the screen

  IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, TRUE );
}

/*===========================================================================

FUNCTION: WB_DisplayDrawScreen

DESCRIPTION:
	This function updates the drawing canvas on the device screen, sets
  the titlebar, updates the scrollbar and draws the drawing cursor
  in the center of the canvas.

  This function is called to place the application in drawing mode.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the application to the APP_STATE_DRAW state.
===========================================================================*/

static void WB_DisplayDrawScreen( CWhiteBoardApp* pApp )
{
  WB_UpdateTitleBar( pApp, IDS_DRAW_MODE );

  // Remove focus from the Icon View menu control

  IMENUCTL_SetActive( pApp->m_pIconViewCtl, FALSE );

  // Set the current viewport and graphics drawing color to white

  IGRAPHICS_SetViewport( pApp->m_pGraphics, &pApp->m_rViewportRect, AEE_GRAPHICS_NONE );
  IGRAPHICS_SetColor( pApp->m_pGraphics, 0, 0, 0, 0 );

  // Initialize the point array

  pApp->m_nNumPoints = 0;

  // Center the pen coordinates on the viewport

  pApp->m_rPenLoc.x = ( pApp->m_rViewportRect.dx / 2 ) + pApp->m_nWindowXOrigin;
  pApp->m_rPenLoc.y = pApp->m_rViewportRect.dy / 2;
//  pApp->m_nPenStepSize = 1;

  // Draw the pen at its initial position on the screen and update the screollbar image

  WB_UpdatePenPosition( pApp, PEN_SHOW, pApp->m_nPenStepSize );
  WB_UpdateScrollBar( pApp );

  // Set the application state

  pApp->m_eAppState = APP_STATE_DRAW;
}

/*===========================================================================

FUNCTION: WB_DisplayMainMenu

DESCRIPTION:
	This function displays the Main Menu (Icon List control) at the bottom
  of the phone display.  The function sets each of the Icon List control
  items, updates the titlebar and sets the application to the 
  APP_STATE_MAIN_MENU state.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the application to the APP_STATE_MAIN_MENU state.
===========================================================================*/

static void WB_DisplayMainMenu( CWhiteBoardApp* pApp )
{
  AEERect rRect;
  CtlAddItem rMenuItem;

  WB_UpdateTitleBar( pApp, IDS_MAIN_MENU );

  // Set the control's properties

  IMENUCTL_SetProperties( pApp->m_pIconViewCtl, MP_ICON_TEXT_TOP | MP_NO_ARROWS );

  // Populate the Icon List items

  rMenuItem.pText = NULL;
  rMenuItem.pImage = NULL;
  rMenuItem.pszResImage = rMenuItem.pszResText = WHITEBOARD_RES_FILE;
  rMenuItem.wFont = AEE_FONT_NORMAL;

  rMenuItem.wText = IDC_DRAW;
  rMenuItem.wImage = pApp->m_nDrawIconID;
  rMenuItem.wItemID = IDC_DRAW;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  rMenuItem.wText = IDC_TOOLS;
  rMenuItem.wImage = IDB_TOOLS;
  rMenuItem.wItemID = IDC_TOOLS;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_HELP;
  rMenuItem.wImage = IDB_HELP;
  rMenuItem.wItemID = IDC_HELP;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  rMenuItem.wText = IDC_ABOUT;
  rMenuItem.wImage = IDB_ABOUT;
  rMenuItem.wItemID = IDC_ABOUT;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  // Set the dimensions and placement of the menu control

  SETAEERECT( &rRect, 0, ( pApp->m_rScreenRect.dy + pApp->m_rScreenRect.y ) - MAIN_MENU_HEIGHT, pApp->m_rScreenRect.dx, MAIN_MENU_HEIGHT );
  IMENUCTL_SetRect( pApp->m_pIconViewCtl, &rRect );

  // Activate the control and draw it on the screen

  IMENUCTL_SetActive( pApp->m_pIconViewCtl, TRUE );

  // Set the application state

  pApp->m_eAppState = APP_STATE_MAIN_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayOptionMenu

DESCRIPTION:
	This function displays the Option Menu screen and sets the application
  state to the APP_STATE_OPTIONS_MENU state.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the applcation to the APP_STATE_OPTIONS_MENU state.
===========================================================================*/

static void WB_DisplayOptionMenu( CWhiteBoardApp* pApp )
{
  CtlAddItem rMenuItem;

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, IDC_TOOLS );

  // Populate the Icon List items

  rMenuItem.pText = NULL;
  rMenuItem.pImage = NULL;
  rMenuItem.pszResImage = rMenuItem.pszResText = WHITEBOARD_RES_FILE;
  rMenuItem.wFont = AEE_FONT_NORMAL;

  rMenuItem.wText = IDC_SHAPES;
  rMenuItem.wImage = IDB_SHAPES;
  rMenuItem.wItemID = IDC_SHAPES;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  rMenuItem.wText = IDC_COLOR;
  rMenuItem.wImage = IDB_COLOR;
  rMenuItem.wItemID = IDC_COLOR;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  // Complete the display of the screen

  IMENUCTL_SetProperties( pApp->m_pIconViewCtl, IMENUCTL_GetProperties( pApp->m_pIconViewCtl ) & ~(MP_ICON_TEXT_TOP) );
  IMENUCTL_SetRect( pApp->m_pIconViewCtl, &pApp->m_rScreenRect );
  IMENUCTL_SetActive( pApp->m_pIconViewCtl, TRUE );

  // Set the application state

  pApp->m_eAppState = APP_STATE_OPTIONS_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayShapeMenu

DESCRIPTION:
	This function displays the Shapes Menu screen and sets the application
  to the APP_STATE_SHAPES_MENU state.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the applcation to the APP_STATE_SHAPES_MENU state.
===========================================================================*/

static void WB_DisplayShapeMenu( CWhiteBoardApp* pApp )
{
  CtlAddItem rMenuItem;

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, IDC_SHAPES );

  // Populate the Icon List items

  rMenuItem.pText = NULL;
  rMenuItem.pImage = NULL;
  rMenuItem.pszResImage = rMenuItem.pszResText = WHITEBOARD_RES_FILE;
  rMenuItem.wFont = AEE_FONT_NORMAL;

  rMenuItem.wText = IDC_LINES;
  rMenuItem.wImage = IDB_LINES;
  rMenuItem.wItemID = IDC_LINES;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_FILLED_SHAPES;
  rMenuItem.wImage = IDB_FILLED_SHAPES;
  rMenuItem.wItemID = IDC_FILLED_SHAPES;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_UNFILLED_SHAPES;
  rMenuItem.wImage = IDB_UNFILLED_SHAPES;
  rMenuItem.wItemID = IDC_UNFILLED_SHAPES;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  // Complete the display of the screen

  IMENUCTL_SetProperties( pApp->m_pIconViewCtl, IMENUCTL_GetProperties( pApp->m_pIconViewCtl ) & ~(MP_ICON_TEXT_TOP) );
  IMENUCTL_SetRect( pApp->m_pIconViewCtl, &pApp->m_rScreenRect );
  IMENUCTL_SetActive( pApp->m_pIconViewCtl, TRUE );

  // Set the application state

  pApp->m_eAppState = APP_STATE_SHAPES_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayLineMenu

DESCRIPTION:
	This function displays the Lines Menu screen and sets the application
  to the APP_STATE_LINES_MENU state.  The Lines Menu allows the user
  to select from one of the types of lines available for drawing to 
  the canvas.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the applcation to the APP_STATE_LINES_MENU state.
===========================================================================*/

static void WB_DisplayLineMenu( CWhiteBoardApp* pApp )
{
  CtlAddItem rMenuItem;

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, IDC_LINES );

  // Populate the Icon List items

  rMenuItem.pText = NULL;
  rMenuItem.pImage = NULL;
  rMenuItem.pszResImage = rMenuItem.pszResText = WHITEBOARD_RES_FILE;
  rMenuItem.wFont = AEE_FONT_NORMAL;

  rMenuItem.wText = IDC_LINE;
  rMenuItem.wImage = IDB_LINE;
  rMenuItem.wItemID = IDC_LINE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  rMenuItem.wText = IDC_POLYLINE;
  rMenuItem.wImage = IDB_POLYLINE;
  rMenuItem.wItemID = IDC_POLYLINE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  rMenuItem.wText = IDC_ARC;
  rMenuItem.wImage = IDB_ARC;
  rMenuItem.wItemID = IDC_ARC;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  // Complete the display of the screen

  IMENUCTL_SetProperties( pApp->m_pIconViewCtl, IMENUCTL_GetProperties( pApp->m_pIconViewCtl ) & ~(MP_ICON_TEXT_TOP) );
  IMENUCTL_SetRect( pApp->m_pIconViewCtl, &pApp->m_rScreenRect );
  IMENUCTL_SetActive( pApp->m_pIconViewCtl, TRUE );

  // Set the application state

  pApp->m_eAppState = APP_STATE_LINES_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayFilledShapeMenu

DESCRIPTION:
	This function displays the Filled Shape Menu screen and sets the application
  to the APP_STATE_FILLED_SHAPES_MENU state.  The Filled Shape Menu allows the 
  user to select from one of the types of filled shapes available for drawing 
  to the canvas.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the applcation to the APP_STATE_FILLED_SHAPES_MENU state.
===========================================================================*/

static void WB_DisplayFilledShapeMenu( CWhiteBoardApp* pApp )
{
  CtlAddItem rMenuItem;

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, IDC_FILLED_SHAPES );

  // Populate the Icon List items

  rMenuItem.pText = NULL;
  rMenuItem.pImage = NULL;
  rMenuItem.pszResImage = rMenuItem.pszResText = WHITEBOARD_RES_FILE;
  rMenuItem.wFont = AEE_FONT_NORMAL;

  rMenuItem.wText = IDC_CIRCLE;
  rMenuItem.wImage = IDB_FILLED_CIRCLE;
  rMenuItem.wItemID = IDC_CIRCLE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_ELLIPSE;
  rMenuItem.wImage = IDB_FILLED_ELLIPSE;
  rMenuItem.wItemID = IDC_ELLIPSE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_RECTANGLE;
  rMenuItem.wImage = IDB_FILLED_RECTANGLE;
  rMenuItem.wItemID = IDC_RECTANGLE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_TRIANGLE;
  rMenuItem.wImage = IDB_FILLED_TRIANGLE;
  rMenuItem.wItemID = IDC_TRIANGLE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_POLYGON;
  rMenuItem.wImage = IDB_FILLED_POLYGON;
  rMenuItem.wItemID = IDC_POLYGON;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  // Complete the display of the screen

  IMENUCTL_SetProperties( pApp->m_pIconViewCtl, IMENUCTL_GetProperties( pApp->m_pIconViewCtl ) & ~(MP_ICON_TEXT_TOP) );
  IMENUCTL_SetRect( pApp->m_pIconViewCtl, &pApp->m_rScreenRect );
  IMENUCTL_SetActive( pApp->m_pIconViewCtl, TRUE );

  // Set the application state

  pApp->m_eAppState = APP_STATE_FILLED_SHAPES_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayUnfilledShapeMenu

DESCRIPTION:
	This function displays the Unfilled Shape Menu screen and sets the application
  to the APP_STATE_UNFILLED_SHAPES_MENU state.  The Unfilled Shape Menu allows the 
  user to select from one of the types of unfilled shapes available for drawing 
  to the canvas.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  Assumes the displayed controls have been previously created and 
  initialized.

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
  Sets the applcation to the APP_STATE_UNFILLED_SHAPES_MENU state.
===========================================================================*/

static void WB_DisplayUnfilledShapeMenu( CWhiteBoardApp* pApp )
{
  CtlAddItem rMenuItem;

  WB_InitScreen( pApp );
  WB_UpdateTitleBar( pApp, IDC_UNFILLED_SHAPES );

  // Populate the Icon List items

  rMenuItem.pText = NULL;
  rMenuItem.pImage = NULL;
  rMenuItem.pszResImage = rMenuItem.pszResText = WHITEBOARD_RES_FILE;
  rMenuItem.wFont = AEE_FONT_NORMAL;

  rMenuItem.wText = IDC_CIRCLE;
  rMenuItem.wImage = IDB_UNFILLED_CIRCLE;
  rMenuItem.wItemID = IDC_CIRCLE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_ELLIPSE;
  rMenuItem.wImage = IDB_UNFILLED_ELLIPSE;
  rMenuItem.wItemID = IDC_ELLIPSE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_PIE;
  rMenuItem.wImage = IDB_UNFILLED_PIE;
  rMenuItem.wItemID = IDC_PIE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_RECTANGLE;
  rMenuItem.wImage = IDB_UNFILLED_RECTANGLE;
  rMenuItem.wItemID = IDC_RECTANGLE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_TRIANGLE;
  rMenuItem.wImage = IDB_UNFILLED_TRIANGLE;
  rMenuItem.wItemID = IDC_TRIANGLE;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );
  
  rMenuItem.wText = IDC_POLYGON;
  rMenuItem.wImage = IDB_UNFILLED_POLYGON;
  rMenuItem.wItemID = IDC_POLYGON;
  IMENUCTL_AddItemEx( pApp->m_pIconViewCtl, &rMenuItem );

  // Complete the display of the screen

  IMENUCTL_SetProperties( pApp->m_pIconViewCtl, IMENUCTL_GetProperties( pApp->m_pIconViewCtl ) & ~(MP_ICON_TEXT_TOP) );
  IMENUCTL_SetRect( pApp->m_pIconViewCtl, &pApp->m_rScreenRect );
  IMENUCTL_SetActive( pApp->m_pIconViewCtl, TRUE );
 
  // Set the application state

  pApp->m_eAppState = APP_STATE_UNFILLED_SHAPES_MENU;
}

/*===========================================================================

FUNCTION: WB_DisplayControl

DESCRIPTION:
	This is a generic function used to display a control on the screen at 
  a specific location.  The function takes the location and dimention
  of the control as arguments, The control label is displayed to the 
  left of the control.  The control is then displayed on the screen
  in the remaining space.
  
  This function is used by the WB_DisplayColorScreen function to position
  its controls on the screen.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

  nTitleID [in] - The resource ID of the titlebar text.

  pCtl [in] - Pointer to the control to be placed on the screen.

  nX [in] - The X coordinate at which the control is to be drawn

  nY [in] - The Y coordinate at which the control is to be drawn

  nWidth [in] - The max number of pixels the control and its label should
    take up on the screen.
    
DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_DisplayControl( CWhiteBoardApp* pApp, uint16 nTitleID, void* pCtl, uint16 nX, uint16 nY, uint16 nWidth )
{
  AEERect rRect;
  AECHAR* psTextBuf;
  int nTextLen;

  // Load the control's label string

  psTextBuf = (AECHAR*) MALLOC( MAX_RES_STRING_BUF_SIZE * sizeof( AECHAR ) );
  ISHELL_LoadResString( pApp->a.m_pIShell, WHITEBOARD_RES_FILE, nTitleID, psTextBuf, MAX_RES_STRING_BUF_SIZE * sizeof( AECHAR ) );

  // Get the number of pixels the label requires to be displayed and use it
  // to render the label to the screen.

  nTextLen = IDISPLAY_MeasureText( pApp->a.m_pIDisplay, AEE_FONT_NORMAL, psTextBuf );
  SETAEERECT( &rRect, nX, nY+1, nWidth, pApp->m_nFontHeight );
  IDISPLAY_DrawText( pApp->a.m_pIDisplay, AEE_FONT_NORMAL, psTextBuf, -1, nX, nY, &rRect, IDF_ALIGN_LEFT | IDF_ALIGN_BOTTOM );
  IDISPLAY_Update( pApp->a.m_pIDisplay );
  FREE( psTextBuf );

  // Set the dimensions and placement of the control so that it is drawn next to the
  // label but is no wider than the number of pixels in the nWidth argument.

  SETAEERECT( &rRect, nX + ( nTextLen + 3 ), nY, nWidth - ( nTextLen + 3 ), pApp->m_nFontHeight );
  
  if( pCtl == pApp->m_pRGBRedCtl || pCtl == pApp->m_pRGBGreenCtl || pCtl == pApp->m_pRGBBlueCtl )
  {
    // CRGBCtl is not derived from IControl so we need to handle these controls
    // via their native API.  Set its location and render it to the screen.

    CRGBCtl_SetRect( (CRGBCtl*) pCtl, &rRect );
    CRGBCtl_Redraw( (CRGBCtl*) pCtl );
  }
  else
  {
    // The control is derived from IControl so set its location and draw it on the screen

    ICONTROL_SetRect( (IControl*) pCtl, &rRect );
    ICONTROL_Redraw( (IControl*) pCtl );
  }
}

//*****************************************************************************
// DRAWING RELATED FUNCTIONS
//****************************************************************************

/*===========================================================================

FUNCTION: WB_DisplaySampleShape

DESCRIPTION:
	This function displays a sample shape scaled to a specified rectangle.  
  The sample shape and fill is determined from the m_eShapeType and 
  m_bShapeFilled variable values in the CWhiteBoardApp global data structure.
  The shape is rendered in a rectangle whose value is calculated prior
  to any drawing operations in the currently selected shape colors.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_DisplaySampleShape( CWhiteBoardApp* pApp )
{
  AEERect rViewportRect;
  AEERect rRect;
  AEEPolygon rPolygon;
  AEEPolyline rPolyline;
  AEELine rLine;
  AEECircle rCircle;
  AEEEllipse rEllipse;
  AEEArc rArc;
  AEEPie rPie;
  AEETriangle rTriangle;
  AEEPoint pPointArray[ 5 ];
  int16 nY, nDY;

  // Calculate the rectangle in which the sample shape is to be
  // displayed.  The rectangle is the full screen width.  The top is
  // just below the title bar and just above the color control.

  nY = pApp->m_rTitleBarRect.y + pApp->m_rTitleBarRect.dy;
  if( pApp->m_nScreenColorDepth < 8 )
  {
    IMENUCTL_GetRect( pApp->m_pColorCtl, &rViewportRect );
  }
  else
  {
    CRGBCtl_GetRect( pApp->m_pRGBRedCtl, &rViewportRect );
  }
  nDY = rViewportRect.y - nY;

  SETAEERECT( &rViewportRect, 0, nY, pApp->m_rScreenRect.dx, nDY - 5 );
  IGRAPHICS_SetPaintMode( pApp->m_pGraphics, AEE_PAINT_COPY );
  IGRAPHICS_SetViewport( pApp->m_pGraphics, &rViewportRect, AEE_GRAPHICS_NONE );
  IGRAPHICS_SetFillMode( pApp->m_pGraphics, (boolean) pApp->m_bFillShape );

  // Set the RGB color value based on the color depth of the phone screen

  WB_SetRGBColor( pApp );

  // Draw the shape based on the currently selected shape type.  Points for each
  // shape are calculated such that the shape will fit within the available
  // rectangle and centered on the screen.

  switch( pApp->m_nShapeType )
  {
    case IDC_LINE:
      rLine.sx = rViewportRect.dx / 3;
      rLine.sy = rViewportRect.dy / 2;
      rLine.ex = rLine.sx * 2;
      rLine.ey = rLine.sy;
      IGRAPHICS_DrawLine( pApp->m_pGraphics, &rLine );
      break;
    case IDC_POLYGON:
    case IDC_POLYLINE:
      pPointArray[ 0 ].x = ( rViewportRect.dx / 8 ) * 2;
      pPointArray[ 0 ].y = rViewportRect.dy / 5;
      pPointArray[ 1 ].x = ( rViewportRect.dx / 8 ) * 3;
      pPointArray[ 1 ].y = pPointArray[ 0 ].y * 3;
      pPointArray[ 2 ].x = ( rViewportRect.dx / 8 ) * 4;
      pPointArray[ 2 ].y = pPointArray[ 0 ].y * 2;
      pPointArray[ 3 ].x = ( rViewportRect.dx / 8 ) * 5;
      pPointArray[ 3 ].y = pPointArray[ 0 ].y * 4;
      pPointArray[ 4 ].x = ( rViewportRect.dx / 8 ) * 6;
      pPointArray[ 4 ].y = pPointArray[ 0 ].y - 6;

      if( pApp->m_nShapeType == IDC_POLYGON )
      {
        rPolygon.len = 5;
        rPolygon.points = pPointArray;
        IGRAPHICS_DrawPolygon( pApp->m_pGraphics, &rPolygon );
      }
      else
      {
        rPolyline.len = 5;
        rPolyline.points = pPointArray;
        IGRAPHICS_DrawPolyline( pApp->m_pGraphics, &rPolyline );
      }
      break;
    case IDC_ARC:
      rArc.cx = rViewportRect.dx / 2;
      rArc.cy = rViewportRect.dy / 2;
      rArc.r = rViewportRect.dy / 4;
      rArc.startAngle = 0;
      rArc.arcAngle = 180;
      IGRAPHICS_DrawArc( pApp->m_pGraphics, &rArc );
      break;
    case IDC_CIRCLE:
      rCircle.cx = rViewportRect.dx / 2;
      rCircle.cy = rViewportRect.dy / 2;
      rCircle.r = rViewportRect.dy / 4;
      IGRAPHICS_DrawCircle( pApp->m_pGraphics, &rCircle );
      break;
    case IDC_ELLIPSE:
      rEllipse.cx = rViewportRect.dx / 2;
      rEllipse.cy = rViewportRect.dy / 2;
      rEllipse.wx = ( rViewportRect.dx / 5 ) * 2;
      rEllipse.wy = rViewportRect.dy / 4;
      IGRAPHICS_DrawEllipse( pApp->m_pGraphics, &rEllipse );
      break;
    case IDC_PIE:
      rPie.cx = rViewportRect.dx / 3;
      rPie.cy = rViewportRect.dy / 2;
      rPie.r = ( rViewportRect.dy / 4 ) * 3;
      rPie.startAngle = 340;
      rPie.arcAngle = 50;
      IGRAPHICS_DrawPie( pApp->m_pGraphics, &rPie );
      break;
    case IDC_RECTANGLE:
      SETAEERECT( &rRect, rViewportRect.dx / 4, rViewportRect.dy / 4, ( rViewportRect.dx / 4 ) * 2, ( rViewportRect.dy / 4 ) * 2 );
      IGRAPHICS_DrawRect( pApp->m_pGraphics, &rRect );
      break;
    case IDC_TRIANGLE:
      rTriangle.x0 = rViewportRect.dx / 2;
      rTriangle.y0 = rViewportRect.dy / 4;
      rTriangle.x1 = rViewportRect.dx / 4;
      rTriangle.y1 = rTriangle.y0 * 3;
      rTriangle.x2 = ( rViewportRect.dx / 4 ) * 3;
      rTriangle.y2 = rTriangle.y0 * 3;
      IGRAPHICS_DrawTriangle( pApp->m_pGraphics, &rTriangle );
      break;
  }
  IGRAPHICS_Update( pApp->m_pGraphics );
}

/*===========================================================================

FUNCTION: WB_UpdatePenPosition

DESCRIPTION:
	This function updates the display of the pen in the drawing viewport 
  on the phone screen.  The pen is displayed as crosshairs with the 
  actual pixel position of the pen coordinates at the center.

  The pen is displayed to the device's drawing buffer and not the 
  actual drawing canvas.  Drawing of the crosshairs is XORed with
  the underlying pixels.  This allows the pen to be "undrawn" by 
  XORing the the crosshairs with the underlying pixels to restore
  the underlying pixels to their original values.  This also prevents
  the pen from being obscured (i.e. black cross hairs drawn on a 
  black background.

  The function first draws the pen in its original position by XORing
  the crosshairs with the original pixel values.  This draws the 
  pen on the screen for the first time and removes the pen in subsequent
  calls to this function.  The pen position is then moved based on the 
  direction specified in the eDirection argument by the number of pixels
  specified in the nStepSize argument.  Finally, the crosshairs are 
  XORed again with the underlying pixels on the screen to render the
  pen on the screen in its new position.

  If the eDirection argument is set to PEN_SHOW, the pen is XORed in
  its original position and not moved.

  This routine does not allow the pen to be drawn beyond the boundary 
  of the currently set graphics viewport.  Attempts to move the pen
  to the left or right beyond the viewport causes the drawing canvas to 
  be shifted accordingly and redisplayed in the viewport.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

  eDirection [in] - Specifies the direction the pen is to be moved relative
    to its current position.
  
  nStepSize [in] - The number of pixels the pen is to move in the direction
    specified in the eDirection argument.

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_UpdatePenPosition( CWhiteBoardApp* pApp, EPenDirectionType eDirection, uint16 nStepSize )
{
  AEELine rLine;

  // Remove the original pen image from the screen

  IGRAPHICS_SetPaintMode( pApp->m_pGraphics, AEE_PAINT_XOR );
  IGRAPHICS_SetColor( pApp->m_pGraphics, 255, 255, 255, 0 );
  rLine.sx = ( pApp->m_rPenLoc.x - PEN_SIZE ) - pApp->m_nWindowXOrigin;
  rLine.sy = pApp->m_rPenLoc.y;
  rLine.ex = ( pApp->m_rPenLoc.x + PEN_SIZE ) - pApp->m_nWindowXOrigin;
  rLine.ey = pApp->m_rPenLoc.y;
  IGRAPHICS_DrawLine( pApp->m_pGraphics, &rLine );
  rLine.sx = pApp->m_rPenLoc.x - pApp->m_nWindowXOrigin;
  rLine.sy = pApp->m_rPenLoc.y - PEN_SIZE;
  rLine.ex = pApp->m_rPenLoc.x - pApp->m_nWindowXOrigin;
  rLine.ey = pApp->m_rPenLoc.y + PEN_SIZE;
  IGRAPHICS_DrawLine( pApp->m_pGraphics, &rLine );

  // Increment or decrement the pen coordinates based on the direction and step size

  switch( eDirection )
  {
  case PEN_SHOW:
    return;
  case PEN_LEFT:

    // Set the new pen position

    pApp->m_rPenLoc.x -= nStepSize;

    // Scroll the canvas in the viewport if needed

    if( pApp->m_rPenLoc.x < pApp->m_nWindowXOrigin )
    {
      if( pApp->m_rPenLoc.x < 0 )
        pApp->m_rPenLoc.x = 0;

      if( pApp->m_nWindowXOrigin == 0 )
        break;

      pApp->m_nWindowXOrigin = pApp->m_rPenLoc.x;

      IDISPLAY_BitBlt( pApp->a.m_pIDisplay, pApp->m_rViewportRect.x, pApp->m_rViewportRect.y, pApp->m_rViewportRect.dx, pApp->m_rViewportRect.dy, pApp->m_pBitmap, pApp->m_nWindowXOrigin, 0, AEE_RO_COPY );
      WB_UpdateScrollBar( pApp );
      IDISPLAY_Update( pApp->a.m_pIDisplay );
    }
    break;
  case PEN_RIGHT:
 
    // Set the new pen position

    pApp->m_rPenLoc.x += nStepSize;

    // Scroll the canvas in the viewport if needed

    if( pApp->m_rPenLoc.x >= pApp->m_nBitmapWidth - 1 )
      pApp->m_rPenLoc.x = pApp->m_nBitmapWidth - 2;

    if( pApp->m_rPenLoc.x >= pApp->m_nWindowXOrigin + ( pApp->m_rViewportRect.dx - 1) )
    {
      pApp->m_nWindowXOrigin = pApp->m_rPenLoc.x - ( pApp->m_rViewportRect.dx - 2 );

      IDISPLAY_BitBlt( pApp->a.m_pIDisplay, pApp->m_rViewportRect.x, pApp->m_rViewportRect.y, pApp->m_rViewportRect.dx, pApp->m_rViewportRect.dy, pApp->m_pBitmap, pApp->m_nWindowXOrigin, 0, AEE_RO_COPY );
      WB_UpdateScrollBar( pApp );
      IDISPLAY_Update( pApp->a.m_pIDisplay );
    }
    break;
  case PEN_UP:

    // Set the new pen position

    pApp->m_rPenLoc.y -= nStepSize;

    // Insure pen does not out of the viewport rectangle

    if( pApp->m_rPenLoc.y < 0 )
      pApp->m_rPenLoc.y = 0;
    break;
  case PEN_DOWN:

    // Set the new pen position

    pApp->m_rPenLoc.y += nStepSize;

    // Insure pen does not out of the viewport rectangle

    if( pApp->m_rPenLoc.y >= pApp->m_rViewportRect.dy )
      pApp->m_rPenLoc.y = pApp->m_rViewportRect.dy - 1;
    break;
  }
  
  // Draw the pen in the new position.

  rLine.sx = ( pApp->m_rPenLoc.x - PEN_SIZE ) - pApp->m_nWindowXOrigin;
  rLine.sy = pApp->m_rPenLoc.y;
  rLine.ex = ( pApp->m_rPenLoc.x + PEN_SIZE ) - pApp->m_nWindowXOrigin;
  rLine.ey = pApp->m_rPenLoc.y;
  IGRAPHICS_DrawLine( pApp->m_pGraphics, &rLine );
  rLine.sx = pApp->m_rPenLoc.x - pApp->m_nWindowXOrigin;
  rLine.sy = pApp->m_rPenLoc.y - PEN_SIZE;
  rLine.ex = pApp->m_rPenLoc.x - pApp->m_nWindowXOrigin;
  rLine.ey = pApp->m_rPenLoc.y + PEN_SIZE;
  IGRAPHICS_DrawLine( pApp->m_pGraphics, &rLine );
  IGRAPHICS_Update( pApp->m_pGraphics );
}

/*===========================================================================

FUNCTION: WB_UpdateVertexDisplay

DESCRIPTION:
	This function updates the display of all points specified in the drawing
  viewport by the user pressing the select button during a drawing operation.
  The function is implemented as a callback, triggered by a timer which
  causes the points to be drawn on the screen.  
  
  Points are rendered to the device screen buffer and not the drawing
  canvas.  Each call to this function causes the points to toggle, giving the
  points a flashing appearance.  The points are drawn on the screen 
  for the first time by calling this function directly, supplying
  the value of the CWhiteBoardApp global data structure as an argument.
  Prior to returning, this funtion sets a callback timer to call itself
  causing the pixels to be undrawn.  The timer is then set again to redraw
  the points...etc.  This facilitates the blinking effect.

  To stop the blinking, the timer needs to be cancelled via the
  ISHELL_CancelTimer API.

PARAMETERS:
	pData [in] - The user data pointer that is passed as the only parameter
    to the callback.  This value should always be the pointer to the 
    CWhiteBoardApp global data structure.

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_UpdateVertexDisplay( void* pData )
{
  CWhiteBoardApp* pApp = (CWhiteBoardApp*) pData;
  int i;
  AEEPoint rPoint;

  // Set the pixel color based on the blink state

  if( pApp->m_bBlinkMode )
  {
    IGRAPHICS_SetColor( pApp->m_pGraphics, 255, 255, 255, 0 );
    pApp->m_bBlinkMode = FALSE;
  }
  else
  {
    IGRAPHICS_SetColor( pApp->m_pGraphics, 0, 0, 0, 0 );
    pApp->m_bBlinkMode = TRUE;
  }

  IGRAPHICS_SetPaintMode( pApp->m_pGraphics, AEE_PAINT_COPY );

  // Render each of the pixels to the screen

  for( i = 0; i < pApp->m_nNumPoints; i++ )
  {
    rPoint.x = pApp->m_pPointList[ i ].x - pApp->m_nWindowXOrigin;
    rPoint.y = pApp->m_pPointList[ i ].y;
    IGRAPHICS_DrawPoint( pApp->m_pGraphics, &rPoint );
  }
  IGRAPHICS_Update( pApp->m_pGraphics );

  // Set the callback timer to call this function and blink the pixels

  ISHELL_SetTimer( pApp->a.m_pIShell, VERTEX_BLINK_TIMER_DURATION, (PFNNOTIFY) WB_UpdateVertexDisplay, pApp );
}

/*===========================================================================

FUNCTION: WB_CommitShape

DESCRIPTION:
	This function draws a shape to the IBitmap object. The shape to be drawn is 
  specified by m_nShapeType variable in the global data structure.  Color 
  values are obtained from the Red, Green and Blue RGBCtls if the screen 
  color depth is 8 bits or greater.  For lower color depths, the color
  is determined from the m_nFillColor and m_nBorderColor varaibles.

  After the shape is rendered to the canvas, the canvas is copied to the 
  current viewport and rendered to the device screen.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_CommitShape( CWhiteBoardApp* pApp )
{
  AEERect rRect;
  AEEPolygon rPolygon;
  AEEPolyline rPolyline;
  AEELine rLine;
  AEECircle rCircle;
  AEEEllipse rEllipse;
  AEEArc rArc;
  AEEPie rPie;
  AEETriangle rTriangle;

  // Stop the vertex blink display timer

  ISHELL_CancelTimer( pApp->a.m_pIShell, (PFNNOTIFY) WB_UpdateVertexDisplay, pApp );

  // Set the graphics target to draw to the bitmap buffer

  IGRAPHICS_SetDestination( pApp->m_pGraphics, pApp->m_pBitmap );
  IGRAPHICS_SetClip( pApp->m_pGraphics, NULL, AEE_GRAPHICS_NONE );
  IGRAPHICS_SetViewport( pApp->m_pGraphics, NULL, AEE_GRAPHICS_NONE );
  IGRAPHICS_Update( pApp->m_pGraphics );

  // Set the fill and paint modes for the graphics drawing operations

  IGRAPHICS_SetFillMode( pApp->m_pGraphics, (boolean) pApp->m_bFillShape );
  IGRAPHICS_SetPaintMode( pApp->m_pGraphics, AEE_PAINT_COPY );

  // Set the RGB color value based on the color depth of the phone screen

  WB_SetRGBColor( pApp );

  // Draw the shape to the canvas

  switch( pApp->m_nShapeType )
  {
    case IDC_LINE:
      rLine.sx = pApp->m_pPointList[0].x;
      rLine.sy = pApp->m_pPointList[0].y;
      rLine.ex = pApp->m_pPointList[1].x;
      rLine.ey = pApp->m_pPointList[1].y;
      IGRAPHICS_DrawLine( pApp->m_pGraphics, &rLine );
      break;
    case IDC_POLYGON:
    case IDC_POLYLINE:
      if( pApp->m_nShapeType == IDC_POLYGON )
      {
        rPolygon.len = pApp->m_nNumPoints;
        rPolygon.points = pApp->m_pPointList;
        IGRAPHICS_DrawPolygon( pApp->m_pGraphics, &rPolygon );
      }
      else
      {
        rPolyline.len = pApp->m_nNumPoints;
        rPolyline.points = pApp->m_pPointList;
        IGRAPHICS_DrawPolyline( pApp->m_pGraphics, &rPolyline );
      }
      break;
    case IDC_ARC:
      rArc.cx = pApp->m_pPointList[0].x;
      rArc.cy = pApp->m_pPointList[0].y;
      rArc.r = ABS_VALUE( pApp->m_pPointList[1].x - pApp->m_pPointList[0].x );
      rArc.startAngle = WB_CalculateAngle( &( pApp->m_pPointList[0] ), &( pApp->m_pPointList[1] ) );
      rArc.arcAngle = WB_CalculateAngle( &( pApp->m_pPointList[0] ), &( pApp->m_pPointList[2] ) );
      if( rArc.arcAngle < rArc.startAngle )
        rArc.arcAngle = rArc.arcAngle + ( 360 - rArc.startAngle );
      else
        rArc.arcAngle = rArc.arcAngle - rArc.startAngle;
      IGRAPHICS_DrawArc( pApp->m_pGraphics, &rArc );
      break;
    case IDC_CIRCLE:
      rCircle.cx = pApp->m_pPointList[0].x;
      rCircle.cy = pApp->m_pPointList[0].y;
      rCircle.r = ABS_VALUE( pApp->m_pPointList[1].x - pApp->m_pPointList[0].x );
      IGRAPHICS_DrawCircle( pApp->m_pGraphics, &rCircle );
      break;
    case IDC_ELLIPSE:
      rEllipse.cx = pApp->m_pPointList[0].x;
      rEllipse.cy = pApp->m_pPointList[0].y;
      rEllipse.wx = ABS_VALUE( pApp->m_pPointList[1].x - pApp->m_pPointList[0].x );
      rEllipse.wy = ABS_VALUE( pApp->m_pPointList[1].y - pApp->m_pPointList[0].y );
      IGRAPHICS_DrawEllipse( pApp->m_pGraphics, &rEllipse );
      break;
    case IDC_PIE:
      rPie.cx = pApp->m_pPointList[0].x;
      rPie.cy = pApp->m_pPointList[0].y;
      rPie.r = ABS_VALUE( pApp->m_pPointList[1].x - pApp->m_pPointList[0].x );
      rPie.startAngle = WB_CalculateAngle( &( pApp->m_pPointList[0] ), &( pApp->m_pPointList[1] ) );
      rPie.arcAngle = WB_CalculateAngle( &( pApp->m_pPointList[0] ), &( pApp->m_pPointList[2] ) );
      if( rPie.arcAngle < rPie.startAngle )
        rPie.arcAngle = rPie.arcAngle + ( 360 - rPie.startAngle );
      else
        rPie.arcAngle = rPie.arcAngle - rPie.startAngle;
      IGRAPHICS_DrawPie( pApp->m_pGraphics, &rPie );
      break;
    case IDC_RECTANGLE:
    case IDC_CLIP_RECT:
      if( pApp->m_pPointList[0].x < pApp->m_pPointList[1].x )
      {
        rRect.x = pApp->m_pPointList[0].x;
        rRect.dx = pApp->m_pPointList[1].x - ( rRect.x - 1 );
      }
      else
      {
        rRect.x = pApp->m_pPointList[1].x;
        rRect.dx = pApp->m_pPointList[0].x - ( rRect.x - 1 );
      }
      if( pApp->m_pPointList[0].y < pApp->m_pPointList[1].y )
      {
        rRect.y = pApp->m_pPointList[0].y;
        rRect.dy = pApp->m_pPointList[1].y - ( rRect.y - 1 );
      }
      else
      {
        rRect.y = pApp->m_pPointList[1].y;
        rRect.dy = pApp->m_pPointList[0].y - ( rRect.y - 1 );
      }
      if( pApp->m_nShapeType == IDC_CLIP_RECT )
      {
        IGRAPHICS_ClearRect( pApp->m_pGraphics, &rRect );
        pApp->m_nShapeType = pApp->m_nShapeTypeBeforeClip;
        WB_UpdateTitleBar( pApp, IDS_DRAW_MODE );
      }
      else
      {
        IGRAPHICS_DrawRect( pApp->m_pGraphics, &rRect );
      }
      break;
    case IDC_TRIANGLE:
      rTriangle.x0 = pApp->m_pPointList[0].x;
      rTriangle.y0 = pApp->m_pPointList[0].y;
      rTriangle.x1 = pApp->m_pPointList[1].x;
      rTriangle.y1 = pApp->m_pPointList[1].y;
      rTriangle.x2 = pApp->m_pPointList[2].x;
      rTriangle.y2 = pApp->m_pPointList[2].y;
      IGRAPHICS_DrawTriangle( pApp->m_pGraphics, &rTriangle );
      break;
  }

  // Draw the canvas buffer to the screen viewport.  Then restore the
  // graphics buffer to the screen buffer.

  IGRAPHICS_Update( pApp->m_pGraphics );

  IGRAPHICS_SetDestination( pApp->m_pGraphics, NULL );
  IGRAPHICS_SetViewport( pApp->m_pGraphics, &pApp->m_rViewportRect, AEE_GRAPHICS_NONE );
  IDISPLAY_BitBlt( pApp->a.m_pIDisplay, pApp->m_rViewportRect.x, pApp->m_rViewportRect.y, pApp->m_rViewportRect.dx, pApp->m_rViewportRect.dy, pApp->m_pBitmap, pApp->m_nWindowXOrigin, 0, AEE_RO_COPY );
  IDISPLAY_Update( pApp->a.m_pIDisplay );
  WB_UpdatePenPosition( pApp, PEN_SHOW, 0 );
}

/*===========================================================================

FUNCTION: WB_SetRGBColor

DESCRIPTION:
	This is a generic function used to set the current color selections
  in the IGraphics object.

  RGB values are determined based on the color depth of the device screen.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 
   
DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Sets the fill and border colors of the IGraphics object.
===========================================================================*/

static void WB_SetRGBColor( CWhiteBoardApp* pApp )
{

  // Set the RGB color value based on the color depth of the phone screen

  switch( pApp->m_nScreenColorDepth )
  {
  case 1:
    // For 1 bit screens, set the colors to either white or black

    IGRAPHICS_SetColor( pApp->m_pGraphics, (uint8) ( 255 * pApp->m_nBorderColor ), (uint8) ( 255 * pApp->m_nBorderColor ), (uint8) ( 255 * pApp->m_nBorderColor ), 0 );
    IGRAPHICS_SetFillColor( pApp->m_pGraphics, (uint8) ( 255 * pApp->m_nFillColor ), (uint8) ( 255 * pApp->m_nFillColor ), (uint8) ( 255 * pApp->m_nFillColor ), 0);
    break;
  case 2:
    // For 2 bit screens, set the colors to white, black, gray1 or gray2

    IGRAPHICS_SetColor( pApp->m_pGraphics, (uint8) ( ( 255 / 3 ) * pApp->m_nBorderColor ), (uint8) ( ( 255 / 3 ) * pApp->m_nBorderColor ), (uint8) ( ( 255 / 3 ) * pApp->m_nBorderColor ), 0 );
    IGRAPHICS_SetFillColor( pApp->m_pGraphics, (uint8) ( ( 255 / 3 ) * pApp->m_nFillColor ), (uint8) ( ( 255 / 3 ) * pApp->m_nFillColor ), (uint8) ( ( 255 / 3 ) * pApp->m_nFillColor ), 0);
    break;
  case 4:
    // For 4 bit screens, set the colors to one of the 16 values

    IGRAPHICS_SetColor( pApp->m_pGraphics, (uint8) ( ( 255 / 16 ) * pApp->m_nBorderColor ), (uint8) ( ( 255 / 16 ) * pApp->m_nBorderColor ), (uint8) ( ( 255 / 16 ) * pApp->m_nBorderColor ), 0 );
    IGRAPHICS_SetFillColor( pApp->m_pGraphics, (uint8) ( ( 255 / 16 ) * pApp->m_nFillColor ), (uint8) ( ( 255 / 16 ) * pApp->m_nFillColor ), (uint8) ( ( 255 / 16 ) * pApp->m_nFillColor ), 0);
    break;
  default:

    // For 8 bit screens and higher, set the RGB values.

    IGRAPHICS_SetColor( pApp->m_pGraphics, CRGBCtl_GetBorderValue( pApp->m_pRGBRedCtl ), CRGBCtl_GetBorderValue( pApp->m_pRGBGreenCtl ), CRGBCtl_GetBorderValue( pApp->m_pRGBBlueCtl ), 0 );
    IGRAPHICS_SetFillColor( pApp->m_pGraphics, CRGBCtl_GetFillValue( pApp->m_pRGBRedCtl ), CRGBCtl_GetFillValue( pApp->m_pRGBGreenCtl ), CRGBCtl_GetFillValue( pApp->m_pRGBBlueCtl ), 0 );
    break;
  }
}

/*===========================================================================

FUNCTION: WB_CalculateAngle

  This function calculates the angle (in degrees) from one point, specifying
  the center of a circle, and a second point.  The angle is returned as
  an integer so accuracy is limited.

DESCRIPTION:

PARAMETERS:

  pOrigin [in] - Specifies the x and y coordinates of the origin of the 
    circle.

  pRadius [in] - Specifies the x and y coordinates of the point which forms
    the angle relative to the origin coordinates.

DEPENDENCIES:
  None

RETURN VALUE:
  Returns the angle (a value from 0 to 360 degrees).

SIDE EFFECTS:
  None
===========================================================================*/

static int16 WB_CalculateAngle( AEEPoint* pOrigin, AEEPoint* pRadius )
{
  AEEPoint rTransRadius;  
  int16 nAngle;

  // Translate the point defining the radius to the origin of the coordinate plane

  rTransRadius.x = pRadius->x - pOrigin->x;
  rTransRadius.y = ( pRadius->y - pOrigin->y ) * -1;

  // The distance of the Y coordinate from the X axis makes up a 90 degree angle with
  // the X axis from the origin.  It is also the opposite side of the angle we wish
  // to find.  Using the known length of these two sides, we use Pythagorean Theorem
  // to determine the length of the hypoteneuse and finally the angle formed by the
  // hypoteneuse and the X axis.

  nAngle = ( ( rTransRadius.y * rTransRadius.y ) * 90 ) / ( ( rTransRadius.x * rTransRadius.x ) + ( rTransRadius.y * rTransRadius.y ) );

  // Determine the quadrant of the translated radius point and determine the total
  // angle based on the calculated angle and the quadrant in which the radius
  // point resides

  if( rTransRadius.x < 0 && rTransRadius.y >= 0 )
    nAngle = 180 - nAngle;
  if( rTransRadius.x < 0 && rTransRadius.y < 0 )
    nAngle = 180 + nAngle;
  if( rTransRadius.x >= 0 && rTransRadius.y < 0 )
    nAngle = 360 - nAngle;

  return (int16) nAngle;
}

//*****************************************************************************
// UTILITY FUNCTIONS
//****************************************************************************

/*===========================================================================

FUNCTION: WB_UpdateScrollBar

DESCRIPTION:
	This function redraws the scroll bar to the device screen.  The 
  rectangle coordinates of the scrollbar are assumed to have been set
  prior to any calls to this routine.  The scrollbar displays rectangle
  containing a black bar.  The length of the black bar indicates the 
  pixel width of the drawing viewport relative to the width of the
  canvas.  The placement of the black bar within the rectangle
  is determined based on the current viewport display coordinates
  relative to the drawing canvas.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  Causes the phone display to be updated.
===========================================================================*/

static void WB_UpdateScrollBar( CWhiteBoardApp* pApp )
{
  uint16 nBarSize;
  uint16 nBarXLoc;
  AEERect rRect;

  // Calculate the width of the bar and the location within the scroll bar

  nBarSize = ( pApp->m_rHScrollBarRect.dx * pApp->m_rViewportRect.dx ) / pApp->m_nBitmapWidth;
  nBarXLoc = ( ( pApp->m_rHScrollBarRect.dx - nBarSize ) * pApp->m_nWindowXOrigin ) / ( pApp->m_nBitmapWidth - pApp->m_rViewportRect.dx );

  // Redraw the scrollbar to the screen

  IDISPLAY_DrawRect( pApp->a.m_pIDisplay, &pApp->m_rHScrollBarRect, RGB_BLACK, RGB_WHITE, IDF_RECT_FRAME | IDF_RECT_FILL );
  SETAEERECT( &rRect, nBarXLoc, pApp->m_rHScrollBarRect.y, nBarSize, pApp->m_rHScrollBarRect.dy );
  IDISPLAY_DrawRect( pApp->a.m_pIDisplay, &rRect, RGB_BLACK, RGB_BLACK, IDF_RECT_FRAME | IDF_RECT_FILL );
}

/*===========================================================================

FUNCTION: WB_UpdateTitleBar

DESCRIPTION:
	This function redraws the title bar with the resource text specified 
  by the resource ID number in the second argument.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

  nResStringID [in] - The ID of the Resource String to be displayed in the
    title bar.

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  None
===========================================================================*/

static void WB_UpdateTitleBar( CWhiteBoardApp* pApp, uint16 nTitleID )
{
  AECHAR* psTextBuf;

  // Load the string resource text

  psTextBuf = (AECHAR*) MALLOC( MAX_RES_STRING_BUF_SIZE * sizeof( AECHAR ) );
  ISHELL_LoadResString( pApp->a.m_pIShell, WHITEBOARD_RES_FILE, nTitleID, psTextBuf, MAX_RES_STRING_BUF_SIZE * sizeof(AECHAR) );

  // Clear the titlebar, render the text and redraw it.

  IDISPLAY_EraseRect( pApp->a.m_pIDisplay, &pApp->m_rTitleBarRect );
  IDISPLAY_DrawText( pApp->a.m_pIDisplay, AEE_FONT_BOLD, psTextBuf, -1, 0, 0, &pApp->m_rTitleBarRect, IDF_ALIGN_LEFT | IDF_ALIGN_MIDDLE | IDF_RECT_INVERT | IDF_TEXT_INVERTED );
  IDISPLAY_Update( pApp->a.m_pIDisplay );
  FREE( psTextBuf );
}

/*===========================================================================

FUNCTION: WB_InitScreen

DESCRIPTION:
	This function clears the device screen and displays the application
  title image at the top of the screen.

PARAMETERS:
	pApp [in] - Pointer to the CWhiteBoardApp structure. This structure contains 
    information specific to this applet. 

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  None

===========================================================================*/

static void WB_InitScreen( CWhiteBoardApp* pApp )
{
  // Set all controls to the inactive state.

  IMENUCTL_Reset( pApp->m_pIconViewCtl );
  IMENUCTL_SetActive( pApp->m_pIconViewCtl, FALSE );
  ISTATIC_Reset( pApp->m_pMainTextCtl );
  ISTATIC_SetActive( pApp->m_pMainTextCtl, FALSE );
  IMENUCTL_SetActive( pApp->m_pModeCtl, FALSE );
  IMENUCTL_Reset( pApp->m_pSoftKeyCtl );
  IMENUCTL_SetActive( pApp->m_pSoftKeyCtl, FALSE );

  if( pApp->m_nScreenColorDepth >= 8 )
  {
    CRGBCtl_SetActive( pApp->m_pRGBRedCtl, FALSE );
    CRGBCtl_SetActive( pApp->m_pRGBGreenCtl, FALSE );
    CRGBCtl_SetActive( pApp->m_pRGBBlueCtl, FALSE );
  }
  else
  {
    ICONTROL_SetActive( (IControl*) pApp->m_pColorCtl, FALSE );
  }

  // Clear the screen

  IDISPLAY_ClearScreen( pApp->a.m_pIDisplay );

  // Draw the application title image at the top of the screen
  
  if( pApp->m_pTitleBarImage )
    IIMAGE_Draw( pApp->m_pTitleBarImage, 0, 0 );

}

/*===========================================================================

FUNCTION: WB_ReleaseObj

  This is a generic function for releasing BREW objects.

  NOTE: CRGBCtl objects must be released via the CRGBCtl_Release
        function.

DESCRIPTION:

PARAMETERS:

  ppObj [in/out] - Pointer to the object pointer that is to be released.
    The pointer value is set to NULL if the object is released successfully.

DEPENDENCIES:
  None

RETURN VALUE:
  None

SIDE EFFECTS:
  None
===========================================================================*/

static void WB_ReleaseObj(void ** ppObj)
{
  if ( ppObj && *ppObj ) 
  {
    (void) IBASE_Release( ( (IBase *) *ppObj ) );
    *ppObj = NULL;
  }
}

