webengine/wmlengine/src/lmgr/src/ImgContainerBox.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 13:32:15 +0300
changeset 68 92a765b5b3e7
parent 0 dd21522fd290
permissions -rw-r--r--
Revision: 201015 Kit: 201018

/*
* Copyright (c) 2003 - 2004 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Box class associated with the handling image maps
*
*/


// INCLUDE FILES
#include "nw_lmgr_imgcontainerboxi.h"
#include "nw_image_cannedimages.h"
#include "nw_image_epoc32cannedimage.h"
#include "nw_image_virtualimage.h"
#include "nw_lmgr_rootbox.h"
#include "nw_lmgr_imagemapbox.h"
#include "nw_text_ucs2.h"
#include "nw_lmgr_activecontainerbox.h"
#include "nw_lmgr_statictablecellbox.h"
#include "nwx_settings.h"
#include "GDIDevicecontext.h"
#include "BrsrStatusCodes.h"

// CONSTANTS
const NW_Ucs2 NW_LMgr_BoxUtils_Truncate[] = {'.','.','.','\0'};
const NW_Text_Length_t NW_LMgr_BoxUtils_Truncate_Len = 3;

// ============================= LOCAL FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// NW_FBox_ImgContainerBox_GetDirection
// Get the dir attribute 
// Returns: void
// -----------------------------------------------------------------------------
//
static
void
NW_FBox_ImgContainerBox_GetDirection(NW_LMgr_Box_t *box,
                                     NW_GDI_FlowDirection_t* dir)
{
  NW_LMgr_Property_t dirProp;
  NW_ASSERT(box != NULL);

  dirProp.type = NW_CSS_ValueType_Token;
  dirProp.value.token = NW_CSS_PropValue_ltr;
  NW_LMgr_Box_GetProperty(box, NW_CSS_Prop_textDirection, &dirProp);
  if (dirProp.value.token == NW_CSS_PropValue_ltr) {
    *dir = NW_GDI_FlowDirection_LeftRight;
  }
  else {
    *dir = NW_GDI_FlowDirection_RightLeft;
  }
}

// -----------------------------------------------------------------------------
static
TBrowserStatusCode
NW_LMgr_ImgContainerBox_DrawBorder(NW_LMgr_Box_t* box,
                                   CGDIDeviceContext* deviceContext,
                                   NW_Bool isBroken,
                                   NW_Uint8 hasFocus)
{
  NW_TRY(status)
  {
    NW_LMgr_FrameInfo_t borderWidth;
    NW_LMgr_FrameInfo_t borderStyle;
    NW_LMgr_FrameInfo_t borderColor;
    NW_LMgr_PropertyValue_t focusBehavior;
    NW_GDI_Color_t initialBgColor = 0;
    NW_GDI_Color_t color = 0;
    NW_LMgr_Property_t backgroundProp;
    NW_GDI_Color_t backgroundColor = 0x00;
    NW_LMgr_Box_t* parentBox = NULL;
    NW_LMgr_IEventListener_t* eventListener;    
    
    NW_ASSERT(box != NULL);

    NW_LMgr_Box_GetBorderWidth (box, &borderWidth, ELMgrFrameAll );
    if( isBroken == NW_TRUE )  
      {
      // to make sure we always draw the border for the broken image link or button,
      // check the border width, if it is 0, set it to 1 
      if (borderWidth.top == 0) 
        {
        borderWidth.top = (NW_GDI_Metric_t)(borderWidth.top + 1);
        borderWidth.bottom = (NW_GDI_Metric_t)(borderWidth.bottom + 1);
        borderWidth.left = (NW_GDI_Metric_t)(borderWidth.left + 1);
        borderWidth.right = (NW_GDI_Metric_t)(borderWidth.right + 1);
        }
      }

    NW_LMgr_Box_GetBorderColor (box, &borderColor);
    NW_LMgr_Box_GetBorderStyle (box, &borderStyle, ELMgrFrameAll );
    initialBgColor = deviceContext->BackgroundColor();
 
    // Modify the look of the box if we're in focus 
    if (hasFocus)
    {
      focusBehavior.token = NW_CSS_PropValue_none;
      (void) NW_LMgr_Box_GetPropertyValue (box, NW_CSS_Prop_focusBehavior,
                                           NW_CSS_ValueType_Token,
                                           &focusBehavior);

      if (focusBehavior.token == NW_CSS_PropValue_growBorder) 
      {
        borderWidth.top = (NW_GDI_Metric_t)(borderWidth.top + 1);
        borderWidth.bottom = (NW_GDI_Metric_t)(borderWidth.bottom + 1);
        borderWidth.left = (NW_GDI_Metric_t)(borderWidth.left + 1);
        borderWidth.right = (NW_GDI_Metric_t)(borderWidth.right + 1);

        if ((borderStyle.top == NW_CSS_PropValue_none) || 
            (borderStyle.top == NW_CSS_PropValue_hidden)) 
        {
          borderStyle.top = NW_CSS_PropValue_solid;
        }

        if ((borderStyle.bottom == NW_CSS_PropValue_none) || 
          (borderStyle.bottom == NW_CSS_PropValue_hidden)) 
        {
          borderStyle.bottom = NW_CSS_PropValue_solid;
        }

        if ((borderStyle.left == NW_CSS_PropValue_none) || 
          (borderStyle.left == NW_CSS_PropValue_hidden)) 
        {
          borderStyle.left = NW_CSS_PropValue_solid;
        }

        if ((borderStyle.right == NW_CSS_PropValue_none) || 
          (borderStyle.right == NW_CSS_PropValue_hidden)) 
        {
          borderStyle.right = NW_CSS_PropValue_solid;
        }
      }
    }

    eventListener = static_cast<NW_LMgr_IEventListener_t*>
      (NW_Object_QueryInterface (box, &NW_LMgr_IEventListener_Class));

    if ((!hasFocus) && (!eventListener))
    {
      backgroundProp.type = NW_CSS_ValueType_Token;
      backgroundProp.value.token = NW_CSS_PropValue_transparent;

      parentBox = NW_LMgr_BoxOf(box->parent);
      while(parentBox != NULL)
      {
        if (NW_Object_IsClass(parentBox, &NW_LMgr_ActiveContainerBox_Class))
        {
          break; //we don't want to swap color for anchors
        }

        status = NW_LMgr_Box_GetProperty(parentBox, NW_CSS_Prop_backgroundColor, 
          &backgroundProp);

        if (status == KBrsrSuccess)
        {
          break;
        }
        parentBox = NW_LMgr_BoxOf(parentBox->parent);
      }

      if (backgroundProp.type == NW_CSS_ValueType_Color) 
      {
        backgroundColor = backgroundProp.value.integer;
        deviceContext->SetBackgroundColor( backgroundColor);
      }
    }

    if (!hasFocus)
    {
    // draw the border 
    color = borderColor.bottom;
    deviceContext->SwitchAgainstBG( &color);
    borderColor.bottom = color;
    color = borderColor.left;
    deviceContext->SwitchAgainstBG( &color);
    borderColor.left = color;
    color = borderColor.right;
    deviceContext->SwitchAgainstBG( &color);
    borderColor.right = color;
    color = borderColor.top;
    deviceContext->SwitchAgainstBG( &color);
    borderColor.top = color;
    }

    status = NW_LMgr_Box_DrawBorder( NW_LMgr_Box_GetDisplayBounds(box), deviceContext, &borderWidth, &borderStyle, &borderColor);
    NW_THROW_ON_ERROR(status);

    deviceContext->SetBackgroundColor( initialBgColor);
  }
  NW_CATCH (status)
  {}
  NW_FINALLY
  {
    return status;
  }
  NW_END_TRY
}

// -----------------------------------------------------------------------------
TBrowserStatusCode
NW_LMgr_ImgContainerBox_DisplayAltText(NW_LMgr_Box_t* box,
                                       NW_Text_t* altText,
                                       CGDIDeviceContext* deviceContext,
                                       NW_GDI_Metric_t space,
                                       NW_GDI_Point2D_t *textPos,
                                       CGDIFont *boxFont) 
{
  NW_Ucs2* storage = NULL;
  NW_Bool freeNeeded = NW_FALSE;
  NW_GDI_Color_t initialBgColor = 0;
  NW_GDI_Color_t initialFgColor = 0;
  NW_LMgr_Property_t backgroundProp;
  NW_GDI_Color_t backgroundColor = 0x00;
  NW_LMgr_Box_t* parentBox = NULL;
  NW_GDI_Color_t color = 0x00;
  NW_GDI_FlowDirection_t dir = NW_GDI_FlowDirection_LeftRight;

  NW_TRY(status)
  {
    NW_Text_Length_t length;
    CGDIFont* font;

    if (altText == NULL)
      {
      NW_THROW_SUCCESS(status);
    }

    /* Get the box font */
    font = boxFont;
    NW_THROW_OOM_ON_NULL(font, status);

    status = deviceContext->SplitText(altText, 
                                      font,
                                            space,
                                            0,
                                            NW_GDI_FlowDirection_LeftRight, 
                                            &length, 
                                            NW_GDI_SplitMode_Clip);

    NW_THROW_ON(status, KBrsrOutOfMemory);

    if ((status == KBrsrSuccess) && /* split succeeded */
         (length < NW_Text_GetCharCount(altText)))
      {
      NW_Ucs2* newAltText;
      NW_Uint16 flags = 0;
      NW_Text_Length_t len = 0;
      NW_Text_Length_t displayLength = 0;
      NW_Text_UCS2_t* truncateText;
      NW_GDI_Dimension2D_t truncateExtent;

      truncateText = NW_Text_UCS2_New ((NW_Ucs2*) NW_LMgr_BoxUtils_Truncate, 
                                       NW_LMgr_BoxUtils_Truncate_Len, 0);
      NW_THROW_OOM_ON_NULL(truncateText, status);

      truncateExtent.height = truncateExtent.width = 0;
      status =
        deviceContext->GetTextExtent ( NW_TextOf(truncateText),
                                       font,
                                       0, 
                                       NW_GDI_FlowDirection_LeftRight, 
                                            &truncateExtent);
      NW_Object_Delete(truncateText);

      length = 0;
      status = deviceContext->SplitText(altText, 
                                        font,
                                              (NW_GDI_Metric_t)(space-truncateExtent.width),
                                              0,
                                              NW_GDI_FlowDirection_LeftRight, 
                                              &length, 
                                              NW_GDI_SplitMode_Clip);
      NW_THROW_ON(status, KBrsrOutOfMemory);

      newAltText = NW_Str_New(length + NW_LMgr_BoxUtils_Truncate_Len);
      NW_THROW_OOM_ON_NULL(newAltText, status);      
    
      flags = NW_Text_Flags_Aligned | NW_Text_Flags_Copy | NW_Text_Flags_NullTerminated;
      storage = NW_Text_GetUCS2Buffer (altText, flags, &len, &freeNeeded);
      NW_THROW_OOM_ON_NULL(storage, status);

      NW_Mem_memset(newAltText, 0, (length+1+NW_LMgr_BoxUtils_Truncate_Len)*sizeof(NW_Ucs2));
      NW_Mem_memcpy(newAltText, storage, length*sizeof(NW_Ucs2));

      NW_Mem_memcpy(newAltText + length, NW_LMgr_BoxUtils_Truncate, 
                    NW_LMgr_BoxUtils_Truncate_Len*sizeof(NW_Ucs2));

      displayLength = (NW_Text_Length_t)(length+NW_LMgr_BoxUtils_Truncate_Len);
      status = NW_Text_SetStorage(altText, newAltText, displayLength, NW_TRUE);
      NW_THROW_ON_ERROR(status);
    }

    initialBgColor = deviceContext->BackgroundColor();
    initialFgColor = deviceContext->ForegroundColor();
    backgroundProp.type = NW_CSS_ValueType_Token;
    backgroundProp.value.token = NW_CSS_PropValue_transparent;

    parentBox = NW_LMgr_BoxOf(box->parent);
    while(parentBox != NULL)
    {
      status = NW_LMgr_Box_GetProperty(parentBox, NW_CSS_Prop_backgroundColor, &backgroundProp);
      if (status == KBrsrSuccess)
        {
        break;
      }
      parentBox = NW_LMgr_BoxOf(parentBox->parent);
    }

    if (backgroundProp.type == NW_CSS_ValueType_Color) 
      {
      backgroundColor = backgroundProp.value.integer;
      deviceContext->SetBackgroundColor( backgroundColor);
    }
    
    /* If the background color of the page is darker than half of the white color 
    indensity, the broken image description text should be white.
    */
    color = deviceContext->ForegroundColor();

    deviceContext->SwitchAgainstBG( &color);

    deviceContext->SetForegroundColor( color);

    NW_FBox_ImgContainerBox_GetDirection(box, &dir);

    status = deviceContext->DrawText( textPos, altText, font, 0, 
      (NW_GDI_TextDecoration_e)0, dir, space);
    NW_THROW_ON_ERROR (status);

    deviceContext->SetForegroundColor( initialFgColor);

    deviceContext->SetBackgroundColor( initialBgColor);
  }
  NW_CATCH (status)
    {
    }
  NW_FINALLY
  {
    if ((storage != NULL) && freeNeeded)
    {
      NW_Mem_Free(storage);
    }
    return status;
  }
  NW_END_TRY
}

// -----------------------------------------------------------------------------
// NW_LMgr_ImgContainerBox_HandleVerticalLayout
// This method implements the image handling rules of vertical layout algorithm 
//   1. scale the image maintaining the aspect ratio if width is greater than display width
//   2. scale the image maintaining the aspect ratio if height is greater than display height
//   3. If image is too small we do not want it to participate in the layout 
// Returns: void
// -----------------------------------------------------------------------------
//
static
void
NW_LMgr_ImgContainerBox_HandleVerticalLayout( NW_LMgr_Box_t *box,
                                              NW_GDI_Dimension2D_t *size,
                                              CGDIDeviceContext* deviceContext )
  {
  const NW_GDI_Rectangle_t* rectangle;
  NW_LMgr_FrameInfo_t padding;
  NW_LMgr_FrameInfo_t borderWidth;
  NW_GDI_Metric_t maxWidth = 0;
  NW_GDI_Metric_t maxHeight = 0;

  /* If image is too small we do not want it to participate in the layout */
  if ((size->width < NW_VerticalLayout_MinImageWidth1) || (size->height < NW_VerticalLayout_MinImageHeight1) ||
      ((size->width < NW_VerticalLayout_MinImageWidth2) && (size->height > NW_VerticalLayout_MinImageHeight2)))
    {
    /* if box is within <a> do not reduce size */
    /* if the box is an imagemap, do not reduce the size */
    if ((NW_LMgr_Box_FindBoxOfClass (box, &NW_LMgr_ActiveContainerBox_Class, NW_FALSE) == NULL)
        && ( !NW_Object_IsInstanceOf(box, &NW_LMgr_ImageMapBox_Class)))
      {
      size->width = 0;
      size->height = 0;    
      }
    }
  else
    {
    rectangle = deviceContext->DisplayBounds();
    NW_LMgr_Box_GetPadding(box, &padding, ELMgrFrameAll );
    NW_LMgr_Box_GetBorderWidth(box, &borderWidth, ELMgrFrameAll );
    maxWidth = (NW_GDI_Metric_t)(rectangle->dimension.width - padding.left - padding.right - borderWidth.right - borderWidth.left - 6);
    maxHeight = (NW_GDI_Metric_t)(rectangle->dimension.width - padding.top - padding.bottom - borderWidth.top - borderWidth.bottom);

    /* scale the image maintaining the aspect ratio if width is greater than display width */
    if (size->width > maxWidth)
      {
      size->height = (NW_GDI_Metric_t)((maxWidth*size->height)/size->width);
      size->width = maxWidth;
      }
    /* scale the image maintaining the aspect ratio if height is greater than display height */
    if (size->height > maxHeight)
      {
      size->width = (NW_GDI_Metric_t)((maxHeight*size->width)/size->height);
      size->height = maxHeight;
      }
    }
  }

/* ------------------------------------------------------------------------- *
   static data
 * ------------------------------------------------------------------------- */

const
NW_LMgr_ImgContainerBox_Class_t  NW_LMgr_ImgContainerBox_Class = {
  { /* NW_Object_Core            */
    /* super                     */ &NW_LMgr_MediaBox_Class,
    /* queryInterface            */ _NW_Object_Base_QueryInterface
  },
  { /* NW_Object_Base            */
    /* interfaceList             */ NULL
  },
  { /* NW_Object_Dynamic         */
    /* instanceSize              */ sizeof (NW_LMgr_ImgContainerBox_t),
    /* construct                 */ _NW_LMgr_ImgContainerBox_Construct,
    /* destruct                  */ _NW_LMgr_ImgContainerBox_Destruct
  },
  { /* NW_LMgr_Box               */
    /* split                     */ _NW_LMgr_ImgContainerBox_Split,
    /* resize                    */ _NW_LMgr_Box_Resize,
    /* postResize                */ _NW_LMgr_Box_PostResize,
    /* getMinimumContentSize     */ _NW_LMgr_ImgContainerBox_GetMinimumContentSize,
    /* hasFixedContentSize       */ _NW_LMgr_MediaBox_HasFixedContentSize,
    /* constrain                 */ _NW_LMgr_Box_Constrain,
    /* draw                      */ _NW_LMgr_ImgContainerBox_Draw,
    /* render                    */ _NW_LMgr_Box_Render,
    /* getBaseline               */ _NW_LMgr_Box_GetBaseline,
    /* shift                     */ _NW_LMgr_Box_Shift,
    /* clone                     */ _NW_LMgr_Box_Clone
  },
  { /* NW_LMgr_MediaBox          */
    /* unused                    */ NW_Object_Unused
  },
  { /* NW_LMgr_ImgContainerBox   */
    /* unused                    */ NW_Object_Unused
  }
};

/* ------------------------------------------------------------------------- *
   virtual methods
 * ------------------------------------------------------------------------- */

// -----------------------------------------------------------------------------
// _NW_LMgr_ImgContainerBox_Construct
// Description:  The constructor.  
// Returns:      TBrowserStatusCode
// -----------------------------------------------------------------------------
//
TBrowserStatusCode
_NW_LMgr_ImgContainerBox_Construct (NW_Object_Dynamic_t* dynamicObject,
                                    va_list* argp)
  {
  NW_LMgr_ImgContainerBox_t* imageBox;

  NW_ASSERT( dynamicObject != NULL );

  /* for convenience */
  imageBox = NW_LMgr_ImgContainerBoxOf( dynamicObject );

  NW_TRY (status) 
    {
    NW_Ucs2* storage = NULL;
    NW_Bool freeNeeded = NW_FALSE;
    NW_Uint16 flags = 0;
    NW_Text_Length_t len = 0;


    /* invoke our superclass constructor */
    status = _NW_LMgr_Box_Construct( dynamicObject, argp );
    NW_THROW_ON_ERROR( status );
   
    /* initialize the member variables */
    imageBox->virtualImage = va_arg( *argp, NW_Image_AbstractImage_t* );
    NW_ASSERT( imageBox->virtualImage != NULL );

    if( imageBox->virtualImage ) 
      {
      NW_GDI_Rectangle_t bounds;
      NW_GDI_Dimension3D_t imageSize;

      bounds.point.x = 0;
      bounds.point.y = 0;
      if( NW_Image_AbstractImage_GetSize( imageBox->virtualImage, &imageSize ) == KBrsrSuccess )
        {
        bounds.dimension.width = imageSize.width;
        bounds.dimension.height = imageSize.height;
        }
      else
        {
        // default image box sizes
        bounds.dimension.width = NW_LMGR_BOX_MIN_WIDTH;
        bounds.dimension.height = NW_LMGR_BOX_MIN_HEIGHT;
        }
      NW_LMgr_Box_SetFormatBounds( NW_LMgr_BoxOf( imageBox ), bounds );
      }
    // alt text
    imageBox->altText = va_arg( *argp, NW_Text_t* );
    if( imageBox->altText )
      {
      /* compress whitespaces */
      flags = NW_Text_Flags_Aligned | NW_Text_Flags_Copy | NW_Text_Flags_NullTerminated;
      flags |= NW_Text_Flags_CompressWhitespace;
      storage = NW_Text_GetUCS2Buffer (imageBox->altText, flags, &len, &freeNeeded);
      NW_THROW_OOM_ON_NULL(storage, status);
      status = NW_Text_SetStorage(imageBox->altText, storage, len, freeNeeded);
      NW_THROW_ON_ERROR(status);
      }
    // broken flag
    imageBox->isBroken = (NW_Bool) va_arg( *argp, NW_Uint32 );
    } 
  NW_CATCH (status) 
    {
    // image container does not take ownership if the construct failed.
    imageBox->virtualImage = NULL;
    imageBox->altText = NULL;
    } 
  NW_FINALLY 
    {
    return status;
    } 
  NW_END_TRY
  }

// -----------------------------------------------------------------------------
// _NW_LMgr_ImgContainerBox_Destruct
// Description:  The destructor.  
// Returns:      void
// -----------------------------------------------------------------------------
//
void
_NW_LMgr_ImgContainerBox_Destruct( NW_Object_Dynamic_t* dynamicObject )
  {
  NW_LMgr_ImgContainerBox_t* thisObj;

  /* for convenience */
  thisObj = NW_LMgr_ImgContainerBoxOf (dynamicObject);
  // destroy members
  NW_Object_Delete( thisObj->virtualImage );
  NW_Object_Delete( thisObj->altText );
  }

// -----------------------------------------------------------------------------
TBrowserStatusCode
_NW_LMgr_ImgContainerBox_GetMinimumContentSize( NW_LMgr_Box_t *box,
                                                NW_GDI_Dimension2D_t *size )
  {
  NW_TRY (status) 
    {
    NW_LMgr_ImgContainerBox_t* imageContainer;
    NW_GDI_Dimension3D_t imageSize;
    CGDIDeviceContext* deviceContext = NULL;
    NW_GDI_Dimension2D_t textSize;
    NW_LMgr_PropertyValue_t sizeVal;
    CGDIFont* font;
    NW_Bool cssHeightSpecified = NW_TRUE;
    NW_Bool cssWidthSpecified = NW_TRUE;
    NW_LMgr_RootBox_t* rootBox = NW_LMgr_Box_GetRootBox( box );

    size->height = 0;
    size->width = 0;
    textSize.height = 0;
    textSize.width = 0;

    /* for convenience */
    imageContainer = NW_LMgr_ImgContainerBoxOf( box );

    // image alt text first
    if( imageContainer->altText && imageContainer->isBroken == NW_TRUE )
      {
      // Get the box font
      font = NW_LMgr_Box_GetFont( box );
      NW_ASSERT( font != NULL );

      deviceContext = NW_LMgr_RootBox_GetDeviceContext( rootBox );

      status = deviceContext->GetTextExtent(  imageContainer->altText, 
                                            font,
                                            0, 
                                            NW_GDI_FlowDirection_LeftRight, 
                                            &textSize );
      NW_THROW_ON_ERROR( status );
      }   
    
    // Get image size
    if( imageContainer->virtualImage )
      {
      status = NW_Image_AbstractImage_GetSize( imageContainer->virtualImage, &imageSize );
      
      NW_THROW_ON_ERROR(status);

      status = NW_LMgr_Box_GetPropertyValue( box, NW_CSS_Prop_height, NW_CSS_ValueType_Px, &sizeVal );
      
      if( status == KBrsrSuccess )
        {
        size->height = (NW_GDI_Metric_t)sizeVal.integer;
        }
      else
        {
        cssHeightSpecified = NW_FALSE;
        }

      status = NW_LMgr_Box_GetPropertyValue( box, NW_CSS_Prop_width, NW_CSS_ValueType_Px, &sizeVal );

      if( status == KBrsrSuccess )
        {
        size->width = (NW_GDI_Metric_t)sizeVal.integer;
        }
      else
        {
        cssWidthSpecified = NW_FALSE;
        }
        
      // Resize images in grid mode tables to fit in their parent's box.
      NW_LMgr_Property_t prop;

      status = NW_LMgr_Box_GetProperty( box, NW_CSS_Prop_gridModeApplied, &prop );
      if( ( status == KBrsrSuccess ) && NW_LMgr_RootBox_GetSmallScreenOn( rootBox ) )
        {
        NW_Int32        parentWidth = 0;
        NW_LMgr_Box_t*  parent;

        // Get the width of the parent.
        parent = box;
        while( ( parent = (NW_LMgr_Box_t*) NW_LMgr_Box_GetParent( parent ) ) != NULL )
          {
          if ( NW_Object_IsClass( parent, &NW_LMgr_StaticTableCellBox_Class ) )
            {
            NW_LMgr_FrameInfo_t  padding;
            NW_LMgr_FrameInfo_t  border;

            parentWidth = parent->iFormatBounds.dimension.width;

            // Subtract the padding and border widths.
            NW_LMgr_StaticTableCellBox_GetPadding( ( NW_LMgr_StaticTableCellBox_t* ) parent,
                &padding);

            NW_LMgr_Box_GetBorderWidth( parent, &border, ELMgrFrameAll );

            parentWidth -= (padding.left + padding.right + border.left + border.right);
            break;
            }
          }

        // If the image is widder than the parent then resize it to fit.
        // The aspect ratio is maintained.
        if( ( parentWidth > 0 ) && ( size->width > parentWidth ) )
          {
          NW_Float32 temp;

          temp = ( ( NW_Float32 ) parentWidth / size->width )  * size->height;

          size->width = parentWidth;
          size->height = ( NW_GDI_Metric_t ) temp;
          }
        }

      if( ( size->height != 0 ) && ( !cssWidthSpecified ) && ( imageSize.height != 0 ) )
        {
        size->width = (NW_GDI_Metric_t)( ( size->height * imageSize.width ) / imageSize.height );
        }
      else if( ( size->height != 0 ) && ( size->width == 0 ) && ( imageSize.height != 0 ) )
        {
        // In order to conform to IE and Opera look and feel, display a 1 pixel wide line
        size->width = 1;
        }

      if( ( size->width != 0) && ( !cssHeightSpecified ) && ( imageSize.width != 0 ) )
        {
        size->height = (NW_GDI_Metric_t)( ( size->width * imageSize.height ) / imageSize.width );
        }
      else if( ( size->width != 0) && ( size->height == 0 ) && ( imageSize.width != 0 ) )
        {
        // In order to conform to IE and Opera look and feel, display a 1 pixel high line
        size->height = 1;
        }
      
      if( ( !cssWidthSpecified ) && ( !cssHeightSpecified ) )
        {
        size->width = (NW_GDI_Metric_t)( imageSize.width + textSize.width );
      
        if( imageSize.height > textSize.height )
          {
          size->height = imageSize.height;
          }
        else
          {
          size->height = textSize.height;
          }
        }
      }  
    // In case of vertical layout is enabled implement the image handling algorithm 
    if( NW_LMgr_RootBox_GetSmallScreenOn( rootBox ) )
      {
      if( deviceContext == NULL )
        {
        deviceContext = NW_LMgr_RootBox_GetDeviceContext( NW_LMgr_Box_GetRootBox( box ) );
        }
      
      NW_LMgr_ImgContainerBox_HandleVerticalLayout( box, size, deviceContext );
      }
    } 
  NW_CATCH (status) 
    {  
    } 
  NW_FINALLY 
    {
    return status;  
    } 
  NW_END_TRY
  }

// -----------------------------------------------------------------------------
TBrowserStatusCode
_NW_LMgr_ImgContainerBox_Split(NW_LMgr_Box_t* box, 
                               NW_GDI_Metric_t space, 
                               NW_LMgr_Box_t* *splitBox,
                               NW_Uint8 flags)
  {
  NW_LMgr_PropertyValue_t sizeVal;
  TBrowserStatusCode status;
  NW_LMgr_ImgContainerBox_t* thisObj;

  NW_ASSERT(box != NULL);

  /* for convenience */
  thisObj = NW_LMgr_ImgContainerBoxOf( box );

  if( thisObj->isBroken )
    {
    *splitBox = NULL;
    /* If box does not fit in the space and the box is not
     on the new line the input box needs to be pushed on the new line */
    NW_GDI_Rectangle_t boxBounds = NW_LMgr_Box_GetFormatBounds( box );
    if ((boxBounds.dimension.width > space) && !(flags & NW_LMgr_Box_SplitFlags_AtNewLine))
      {
      return KBrsrLmgrNoSplit;
      }

    if(boxBounds.dimension.width <= space) 
      {
      return KBrsrSuccess;
      }

    /* we are more than space */

    /* if width was specified we do not want to change it */
    status = NW_LMgr_Box_GetPropertyValue(box, NW_CSS_Prop_width, NW_CSS_ValueType_Px, &sizeVal);
    if (status == KBrsrSuccess)
      {
      return status;
      }
    boxBounds.dimension.width = space;
    NW_LMgr_Box_SetFormatBounds( box, boxBounds );
    }
  else
    {
    // real image split is handled by base class    
    return NW_LMgr_Box_Class.NW_LMgr_Box.split(box, space, splitBox, flags );
    }
  return KBrsrSuccess;
  }

// -----------------------------------------------------------------------------
TBrowserStatusCode
_NW_LMgr_ImgContainerBox_Draw( NW_LMgr_Box_t* box,
                               CGDIDeviceContext* deviceContext,
                               NW_Uint8 hasFocus )
  {
  NW_TRY( status ) 
    {
    NW_LMgr_ImgContainerBox_t* imageContainer;

    NW_ASSERT( box != NULL );
    
    imageContainer = NW_LMgr_ImgContainerBoxOf( box );
    // height or width == 0 implies that there is no need to draw the image
    // as the box does not occupy any space on displa.  The box is also not drawn
    // if the box hasn't been "placed".
    NW_GDI_Rectangle_t boxBounds = NW_LMgr_Box_GetDisplayBounds( box );

    if ((boxBounds.dimension.width == 0) || (boxBounds.dimension.height == 0) ||
        !imageContainer->iBoxPlaced)
      {
      NW_THROW_SUCCESS(status);
      }
    
    /* invoke our superclass draw method first */
    status = _NW_LMgr_Box_Draw( box, deviceContext, hasFocus );
    NW_THROW_ON_ERROR( status );

    status = NW_LMgr_ImgBox_DrawContent( box, deviceContext, imageContainer->virtualImage,
      imageContainer->isBroken, imageContainer->altText, hasFocus ); 
    NW_THROW_ON_ERROR( status );
    } 
  NW_CATCH( status ) 
    {
    }
  NW_FINALLY 
    {
    return status;
    } 
  NW_END_TRY
  }

/* ------------------------------------------------------------------------- *
   public final methods
 * ------------------------------------------------------------------------- */

// -----------------------------------------------------------------------------
void
NW_LMgr_ImgContainerBox_ReplaceBrokenImage( NW_LMgr_Box_t* box, NW_Image_AbstractImage_t* image )
  {
  NW_LMgr_ImgContainerBox_t* thisObj;

  NW_ASSERT( box != NULL );

  /* for convenience */
  thisObj = NW_LMgr_ImgContainerBoxOf( box );

  // free old image
  NW_Object_Delete( thisObj->virtualImage );
  // and replace it
  thisObj->virtualImage = image;  
  // update broken flag
  thisObj->isBroken = NW_FALSE;
  }

NW_Bool
NW_LMgr_ImgContainerBox_ImageIsBroken( NW_LMgr_Box_t* box )
  {
  NW_LMgr_ImgContainerBox_t* thisObj;

  NW_ASSERT( box != NULL );

  /* for convenience */
  thisObj = NW_LMgr_ImgContainerBoxOf( box );

  return thisObj->isBroken;
  }

void
NW_LMgr_ImgContainerBox_ImageIsPlaced(NW_LMgr_Box_t* box)
  {
  NW_LMgr_ImgContainerBox_t* thisObj;

  NW_ASSERT( box != NULL );

  /* for convenience */
  thisObj = NW_LMgr_ImgContainerBoxOf( box );

  thisObj->iBoxPlaced = NW_TRUE;
  }


/* ------------------------------------------------------------------------- *
  convenience functions
 * ------------------------------------------------------------------------- */

// -----------------------------------------------------------------------------
NW_LMgr_ImgContainerBox_t*
NW_LMgr_ImgContainerBox_New (NW_ADT_Vector_Metric_t numProperties, NW_Image_AbstractImage_t* virtualImage, 
                             NW_Text_t* altText, NW_Bool brokenImage )
{
  return (NW_LMgr_ImgContainerBox_t*)
    NW_Object_New (&NW_LMgr_ImgContainerBox_Class, numProperties, virtualImage, altText, (NW_Uint32)brokenImage );
}

/* ------------------------------------------------------------------------- *
  global utility functions
 * ------------------------------------------------------------------------- */

// -----------------------------------------------------------------------------
TBrowserStatusCode
NW_LMgr_ImgBox_DrawContent( NW_LMgr_Box_t* box,
                            CGDIDeviceContext* deviceContext,
                            NW_Image_AbstractImage_t* image,
                            NW_Bool isBroken,
                            NW_Text_t* altText,
                            NW_Uint8 hasFocus)
{
  NW_TRY( status ) 
  {
    NW_GDI_Point2D_t imagePos;
    NW_GDI_Point2D_t textPos;
    NW_GDI_Dimension3D_t imageSize;
    NW_GDI_Rectangle_t innerRect;
    CGDIFont* font = NW_LMgr_Box_GetFont (box);

    NW_ASSERT( box != NULL );
    NW_ASSERT( image != NULL );

    NW_LMgr_RootBox_t* rootBox = NW_LMgr_Box_GetRootBox(box);
    NW_ASSERT(rootBox);

    NW_HED_AppServices_t* appServices = rootBox->appServices;
    NW_ASSERT(appServices);

    // if we are in image map view, with a broken image, just draw the border
    if (appServices->browserAppApi.IsImageMapView() && isBroken)
      {
        status = NW_LMgr_ImageMapBox_DrawBorder( box, deviceContext);
        NW_THROW_ON_ERROR(status);     
      }
    else
      {
        // draw image
        if( image )
        {
          // make our image draw itself at the specified location
          deviceContext->SetPaintMode(  NW_GDI_PaintMode_Copy );
          // get image size 
          NW_LMgr_Property_t hProp;
          NW_LMgr_Property_t wProp;
          
          NW_LMgr_Box_GetInnerRectangle( box, &innerRect );

          if( (NW_LMgr_Box_GetProperty( box, NW_CSS_Prop_height, &hProp ) == KBrsrSuccess) &&
              (NW_LMgr_Box_GetProperty( box, NW_CSS_Prop_width, &wProp ) == KBrsrSuccess) )
            {
            imageSize.width  = wProp.value.integer;        
            imageSize.height = hProp.value.integer;
            }
          else
            {
            status = NW_Image_AbstractImage_GetSize( image, &imageSize );
            NW_THROW_ON_ERROR(status);
            }
  
          NW_LMgr_Box_GetInnerRectangle( box, &innerRect );
        
          // do not stretch out broken images
          if( isBroken == NW_TRUE )  
          {
            if( innerRect.dimension.height < imageSize.height )
            {
              imageSize.height = innerRect.dimension.height;
            }
            if( innerRect.dimension.width < imageSize.width )
            {
              imageSize.width = innerRect.dimension.width;
            }
          }
          else
          {
            imageSize.width = innerRect.dimension.width;
            imageSize.height = innerRect.dimension.height;
            imageSize.depth = 0; 
          }

          imagePos.x = innerRect.point.x;
          imagePos.y = innerRect.point.y;
    
          status = NW_Image_AbstractImage_DrawScaled( image, 
                                                      deviceContext,
                                                      &imagePos, 
                                                      &imageSize );
          NW_THROW_ON_ERROR(status);

          // draw altext
          if( isBroken == NW_TRUE && 
              altText && innerRect.dimension.width > imageSize.width )
          {
            textPos.x = (NW_GDI_Metric_t)(imagePos.x + imageSize.width);
            textPos.y = imagePos.y;

              status = NW_LMgr_ImgContainerBox_DisplayAltText(box, 
                                                              altText, 
                        deviceContext, 
                        (NW_GDI_Metric_t)(innerRect.dimension.width - imageSize.width),
                                                              &textPos,
                                                              font);
            NW_THROW_ON_ERROR (status);
          }
        }

        /* draw the border */
        if( NW_Object_IsClass(box, &NW_LMgr_ImageMapBox_Class))
        {
          status = NW_LMgr_ImageMapBox_DrawBorder(box, deviceContext);
          NW_THROW_ON_ERROR(status);
        }
        else
        {
          status = NW_LMgr_ImgContainerBox_DrawBorder( box, deviceContext, isBroken, hasFocus );
          NW_THROW_ON_ERROR(status);    
        } 
      }
    }
  NW_CATCH( status ) 
  {
  }  
  NW_FINALLY 
  {
    return status;
  }  
  NW_END_TRY
}