/*
* Copyright (c) 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 to handle Netscape plugins
*
*/


// INCLUDE FILES
#include <StringLoader.h> 

#include "nwx_settings.h"

#include "LMgrObjectBox.h"
#include "LMgrObjectBoxListener.h"
#include "LMgrObjectBoxOOC.h"

#include "nw_image_cannedimages.h"
#include "nw_lmgr_activecontainerbox.h"
#include "nw_lmgr_containerbox.h"
#include "LMgrBoxTreeListener.h"
#include "nw_lmgr_rootbox.h"
#include "nw_text_ucs2.h"
#include "nw_text_abstracti.h"


// ============================ LOCAL DEFINES ==================================
#define KDefaultObjectHeight 100
#define KDefaultObjectWidth 100


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CLMgrObjectBox::CLMgrObjectBox
//
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CLMgrObjectBox::CLMgrObjectBox(NW_LMgr_Box_t* aBox) :
        
        iBox(aBox),
        iShowingPlaceHolder(ETrue),
        iCalculatePlaceHolderClip(ETrue),
        iDestructionListeners(2)
    {
    NW_ASSERT(aBox != NULL);
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::ConstructL
//
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::ConstructL(void)
    {
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::~CLMgrObjectBox
//
// Deconstructor.
// -----------------------------------------------------------------------------
//
CLMgrObjectBox::~CLMgrObjectBox()
    {
    // Clean up the place holder
    delete iOrigPlaceHolderText;
    delete iPlaceHolderText;

    // Detach the CPluginInst
    DetachPluginInst();

    // Notify the owners that the box is being destroyed.
    for (TInt i = 0; i < iDestructionListeners.Count(); i++)
        {
        iDestructionListeners[i]->Destroyed();
        }

    iDestructionListeners.Reset();
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::NewL
//
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CLMgrObjectBox* CLMgrObjectBox::NewL(NW_LMgr_Box_t* aBox)
    {
    CLMgrObjectBox* self = new(ELeave) CLMgrObjectBox(aBox);

    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::NewActiveBox
//
// Creates a Object box-tree, an active container box with a single ObjectBox as a child.
// -----------------------------------------------------------------------------
//
TBrowserStatusCode CLMgrObjectBox::NewBox(NW_LMgr_EventHandler_t* aEventHandler, 
        NW_LMgr_Box_t** aPluginContainerBox, NW_LMgr_Box_t** aPluginBox)
    {
    NW_LMgr_Box_t*  containerBox = NULL;
    NW_LMgr_Box_t*  pluginBox = NULL;

    NW_ASSERT(aPluginContainerBox != NULL);
    NW_ASSERT(aPluginBox != NULL);
    TBool inHyperLink = EFalse;

    NW_TRY (status) 
        {
        if (aEventHandler)
            {
            // Create an ActiveContainer box to hold the boxtree, setting the
            // action type for object box.
            containerBox = (NW_LMgr_Box_t*) NW_LMgr_ActiveContainerBox_New(
                0, aEventHandler, NW_LMgr_ActionType_OpenObject);
            NW_THROW_OOM_ON_NULL(containerBox, status);
            aEventHandler = NULL;
            }
        else
            {
            inHyperLink = ETrue;
            containerBox = (NW_LMgr_Box_t*) NW_LMgr_ContainerBox_New(0);
            NW_THROW_OOM_ON_NULL(containerBox, status);
            }

        // Create the CLMgrObjectBox
        pluginBox = (NW_LMgr_Box_t*) NW_LMgr_ObjectBox_New(0);
        NW_THROW_OOM_ON_NULL(pluginBox, status);
        NW_LMgr_ObjectBox_GetObjectBox((NW_LMgr_ObjectBox_t&)(*pluginBox))->iInHyperLink = inHyperLink;

        // Add the pluginBox to the activeContainerBox
        status = NW_LMgr_ContainerBox_AddChild(NW_LMgr_ContainerBoxOf(containerBox), pluginBox);
        NW_THROW_ON_ERROR(status);
        
        *aPluginBox = pluginBox;
        pluginBox = NULL;
        }

    NW_CATCH (status) 
        {
        if (NW_Object_IsInstanceOf(containerBox, &NW_LMgr_ActiveContainerBox_Class))
            {
            // Orphan aEventHandler before deleting the active container box.
            ((NW_LMgr_ActiveContainerBox_t*) containerBox)->eventHandler = NULL;
            }
        NW_Object_Delete(containerBox);  

        NW_Object_Delete(pluginBox);

        containerBox = NULL;
        *aPluginBox = NULL;
        }
    
    NW_FINALLY 
        {
        *aPluginContainerBox = containerBox;
        return status;
        } NW_END_TRY
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::Split
//
// Allows the box to be placed on the on a new line and grows
// the box to the width of "space" if the box doesn't have 
// a width property.
// -----------------------------------------------------------------------------
//
TBrowserStatusCode CLMgrObjectBox::Split(NW_GDI_Metric_t aSpace, 
        NW_LMgr_Box_t** aSplitBox, NW_Uint8 aFlags)
    {
    TBrowserStatusCode status;
    NW_LMgr_PropertyValue_t sizeVal;

    NW_ASSERT(aSplitBox != NULL);

    *aSplitBox = NULL;

    // If box does not fit in the space and the box is not on the new line the 
    // box needs to be pushed on the new line.
    if ((iBox->iFormatBounds.dimension.width > aSpace) && !(aFlags & NW_LMgr_Box_SplitFlags_AtNewLine))
        {
        return KBrsrLmgrNoSplit;
        }

    // If width was specified we do not want to change it.
    status = NW_LMgr_Box_GetPropertyValue(iBox, NW_CSS_Prop_width, NW_CSS_ValueType_Px, &sizeVal);
    if (status == KBrsrSuccess)
        {
        return KBrsrSuccess;
        }

    // Get the box's padding and margin
    NW_LMgr_FrameInfo_t  info;
    TInt                 padding = 0;
    TInt                 border = 0;
    TInt                 margin = 0;

    // NW_LMgr_Box_GetPadding(iBox, &info);
    //padding = info.right;

    // Use the parent's border because it has the border not the object box.
    NW_ASSERT(NW_Object_IsInstanceOf (iBox->parent, &NW_LMgr_ActiveContainerBox_Class) ||
        NW_Object_IsInstanceOf (iBox->parent, &NW_LMgr_ContainerBox_Class));
    NW_LMgr_Box_GetBorderWidth((NW_LMgr_Box_t*) iBox->parent, &info, ELMgrFrameRight );
    border = info.right;

    NW_LMgr_Box_GetMargins(iBox, &info, NULL, ELMgrFrameRight );
    if (info.right != NW_LMgr_FrameInfo_Auto)
        {
        margin = info.right;
        }

    // Set the box's width to the available space minus the border and padding.
    aSpace -= (border + padding + margin);
    iBox->iFormatBounds.dimension.width = aSpace;

    return KBrsrSuccess;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::GetMinimumContentSizeL
//
// Calculates the minimum size needed for the box.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::GetMinimumContentSize(NW_GDI_Dimension2D_t& aSize)
    {
    NW_LMgr_PropertyValue_t sizeVal;

    // Set the default size
    aSize.height = KDefaultObjectHeight;
    aSize.width = KDefaultObjectWidth;

    // Adjust the default size if the width or height props are present.
    if (NW_LMgr_Box_GetPropertyValue(iBox, NW_CSS_Prop_width, 
            NW_CSS_ValueType_Px, &sizeVal) == KBrsrSuccess)
        {
        aSize.width = (NW_GDI_Metric_t) sizeVal.integer;
        }

    if (NW_LMgr_Box_GetPropertyValue(iBox, NW_CSS_Prop_height, 
                NW_CSS_ValueType_Px, &sizeVal) == KBrsrSuccess)
        {
        aSize.height = (NW_GDI_Metric_t) sizeVal.integer;
        }

    // If the browser is in small screen mode scale aSize to fit on the screen
    NW_LMgr_RootBox_t* rootBox = NW_LMgr_Box_GetRootBox( iBox );

    if( NW_LMgr_RootBox_GetSmallScreenOn( rootBox ) )
        {
        NW_GDI_Dimension2D_t aOrigSize = aSize;

        // Scale aSize to the screen.
        HandleVerticalLayout(aOrigSize, aSize);
        }
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::Draw
//
// Draws the border, background and place holder (while the place
// holder is shown).
// -----------------------------------------------------------------------------
//
TBrowserStatusCode CLMgrObjectBox::Draw(const CGDIDeviceContext& aDeviceContext,
        NW_Bool aHasFocus)
    {
    CGDIDeviceContext*      deviceContext = NULL;
    NW_GDI_Dimension3D_t    imageSize;
    NW_GDI_Rectangle_t      innerRect;
    NW_GDI_Point2D_t        pos;
    NW_GDI_FlowDirection_t  dir = NW_GDI_FlowDirection_LeftRight;
    CGDIFont*               font = NULL;
    NW_GDI_Color_t          oldForgroundColor = 0;
    NW_GDI_Color_t          color;
    NW_LMgr_PropertyValue_t value;

    NW_TRY (status)
        {
        // For convenience.
        deviceContext = CONST_CAST(CGDIDeviceContext*, &aDeviceContext);

        // Get the initial colors -- must be done before any throw statement...
        oldForgroundColor = deviceContext->ForegroundColor();        
        
        // Invoke our superclass
        status = NW_LMgr_MediaBox_Class.NW_LMgr_Box.draw(iBox, 
                deviceContext, aHasFocus);
        NW_THROW_ON_ERROR(status);

        // If the place holder isn't being shown then its done.
        if (!iShowingPlaceHolder)
            {
            if (iBoxListener)
                {
                if (iPluginAttached)
                    {
                    iBoxListener->RefreshPlugin();
                    }
                }
            NW_THROW_SUCCESS(status);
            }

        // If necessary get the image place holder.
        if (!iPlaceHolderImage)
            {
            NW_LMgr_RootBox_t*  rootBox = NULL;

            rootBox = NW_LMgr_Box_GetRootBox(iBox);
            NW_THROW_ON_NULL(rootBox, status, KBrsrUnexpectedError);
            if (!iInHyperLink)
                {
                iPlaceHolderImage = NW_Image_CannedImages_GetImage(
                        rootBox->cannedImages, NW_Image_Object);
                }
            else
                {
                // If the <object> is in a hyperlink, treat it as if it was an image
                iPlaceHolderImage = NW_Image_CannedImages_GetImage(
                        rootBox->cannedImages, NW_Image_Broken);
                }
            NW_THROW_ON_NULL((void *) iPlaceHolderImage, status, KBrsrUnexpectedError);
            }

        // Get the rect it is allowed to draw in
        NW_LMgr_Box_GetInnerRectangle(iBox, &innerRect);

        // Get the image size.
        status = NW_Image_AbstractImage_GetSize(iPlaceHolderImage, &imageSize);
        NW_THROW_ON_ERROR(status);

        if (innerRect.dimension.width < imageSize.width)
            {
            imageSize.width = innerRect.dimension.width;
            }

        if (innerRect.dimension.height < imageSize.height)
            {
            imageSize.height = innerRect.dimension.height;
            }

        // If necessary recalculate the clipped place holder text.
        if (iOrigPlaceHolderText && iCalculatePlaceHolderClip)
            {
            // Discard the old text
            delete iPlaceHolderText;
            iPlaceHolderText = NULL;

            iPlaceHolderText = ClipText(*iOrigPlaceHolderText, *deviceContext,
                    innerRect.dimension.width - imageSize.width, ETrue);

            iCalculatePlaceHolderClip = EFalse;
            }

        // Draw Image place holder.
        pos.x = innerRect.point.x;
        pos.y = innerRect.point.y;
    
        status = NW_Image_AbstractImage_DrawScaled(
                CONST_CAST(NW_Image_AbstractImage_t *, iPlaceHolderImage), 
                deviceContext, &pos, &imageSize);
        NW_THROW_ON_ERROR(status);

        // Draw the text only if the <object> is not in hyperlink
        if (!iInHyperLink)
            {
            font = NW_LMgr_Box_GetFont(iBox);
            NW_THROW_ON_NULL(font, status, KBrsrUnexpectedError);
                
            // Set the foreground color
            status = NW_LMgr_Box_GetPropertyValue(iBox, NW_CSS_Prop_color,
                    NW_CSS_ValueType_Color, &value);

            if (status == KBrsrSuccess)
                {
                color = value.integer;

                deviceContext->SwitchAgainstBG(&color);

                deviceContext->SetForegroundColor(color);
                }


            // Draw the text place holder.
            if (iPlaceHolderText)
                {
                NW_Text_UCS2_t nwText;
                NW_GDI_Rectangle_t oldClip = {{0, 0}, {0, 0}};

                status = NW_Text_UCS2_Initialize(&nwText, (void*) iPlaceHolderText->Ptr(), 
                        iPlaceHolderText->Length(), 0);
                NW_THROW_ON_ERROR (status);

                pos.x = innerRect.point.x + imageSize.width;
                pos.y = innerRect.point.y;
    
                /* Save the old clip rect */
                oldClip = aDeviceContext.ClipRect();

                /* Clip to the inner rect so the text doesn't spill outside of the rect */
                const_cast<CGDIDeviceContext &>(aDeviceContext).SetClipRect(&innerRect);

                status = deviceContext->DrawText(&pos, (NW_Text_t*)(&nwText), font, 0, 
                        (NW_GDI_TextDecoration_e) 0, dir, 
                        innerRect.dimension.width - imageSize.width);

                /* Restore the clip BEFORE checking the status */
                const_cast<CGDIDeviceContext &>(aDeviceContext).SetClipRect(&oldClip);

                NW_THROW_ON_ERROR (status);
                }
            }
        }

    NW_CATCH (status) 
        {
        } 

    NW_FINALLY 
        {
        deviceContext->SetForegroundColor(oldForgroundColor);

        return status;
        } 
    NW_END_TRY
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::Render
//
// If necessary it positions the CPluginInst to match the box's 
// bounds and renders the box.
// -----------------------------------------------------------------------------
//
TBrowserStatusCode CLMgrObjectBox::Render(const CGDIDeviceContext& aDeviceContext,
        const NW_LMgr_RootBox_t& aRootBox, const NW_GDI_Rectangle_t& aClipRect, 
        const NW_LMgr_Box_t* aCurrentBox, NW_Uint8 aFlags, NW_Bool& aHasFocus, 
        NW_Bool& aSkipChildren)
    {
    NW_TRY (status)
        {
        // Invoke our superclass
        status = NW_LMgr_MediaBox_Class.NW_LMgr_Box.render(iBox, 
                CONST_CAST(CGDIDeviceContext*, &aDeviceContext), 
                CONST_CAST(NW_GDI_Rectangle_t*, &aClipRect), CONST_CAST(NW_LMgr_Box_t*, 
                aCurrentBox), aFlags, &aHasFocus, &aSkipChildren, 
                CONST_CAST(NW_LMgr_RootBox_t*, &aRootBox));
        NW_THROW_ON_ERROR(status);

        // The PluginInst must be synchronized with the box's new scroll adjusted 
        // location -- the size of the PluginInst may also need to be adjusted.
        // This method is called even if the place holder is shown because it may
        // need to recalculate the clip of the place holder text.
        SyncPluginInstBounds(aDeviceContext);
        }

    NW_CATCH (status) 
        {
        } 

    NW_FINALLY 
        {
        return status;
        } 
    NW_END_TRY
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::SetBoxListener
//
// Sets the box's listener
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::SetBoxListener(MLMgrObjectBoxListener* aListener)
    {
    iBoxListener = aListener;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::AddBoxDestructionListenerL
//
// Adds a box's destruction listener
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::AddBoxDestructionListenerL(MLMgrBoxDestructionListener* aListener)
    {
    User::LeaveIfError(iDestructionListeners.Append(aListener));
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::RemoveBoxDestructionListenerL
//
// Removes a box's destruction listener
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::RemoveBoxDestructionListener(MLMgrBoxDestructionListener* aListener)
    {
    TInt index;

    index = iDestructionListeners.Find(aListener);
    if (index != KErrNotFound)
        {
        iDestructionListeners.Remove(index);
        }
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::SetPlaceHolderTextL
//
// Sets the place holder text.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::SetPlaceHolderTextL(const TDesC& aNewText)
    {
    // Set the place holder state to true
    iShowingPlaceHolder = ETrue;

    // Delete the old text
    delete iOrigPlaceHolderText;
    iOrigPlaceHolderText = NULL;

    delete iPlaceHolderText;
    iPlaceHolderText = NULL;

    // Copy the text
    HBufC*  temp;

    temp = HBufC::NewL(aNewText.Length() + 1);
    temp->Des().Append(aNewText);
    temp->Des().ZeroTerminate();

    iOrigPlaceHolderText = temp;

    iCalculatePlaceHolderClip = ETrue;

    // Invalidate the box
    (void) NW_LMgr_Box_Refresh((NW_LMgr_Box_t*) iBox->parent);
    }

// -----------------------------------------------------------------------------
// CXHTMLObjectElementHandler::SetPlaceHolderTextL
//
// Set the place-holder text to the given resource id.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::SetPlaceHolderTextL(TInt aResourceId)
    {
    HBufC*  placeHolderText = NULL;

    // Get the string from the StringLoader.
    placeHolderText = StringLoader::LoadLC(aResourceId);

    // Set the place holder text.
    SetPlaceHolderTextL(*placeHolderText);
    CleanupStack::PopAndDestroy(placeHolderText);
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::RemovePlaceHolder
//
// Removes the place holder's text and image.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::RemovePlaceHolder(void)
    {
    CGDIDeviceContext*   deviceContext = NULL;
    NW_LMgr_RootBox_t*   rootBox = NULL;

    rootBox = NW_LMgr_Box_GetRootBox(iBox);
    NW_ASSERT(rootBox != NULL);

    deviceContext = NW_LMgr_RootBox_GetDeviceContext(rootBox);
    NW_ASSERT(deviceContext != NULL);
    // Set the place holder state to false
    iShowingPlaceHolder = EFalse;

    // Delete the old text
    delete iOrigPlaceHolderText;
    iOrigPlaceHolderText = NULL;

    delete iPlaceHolderText;
    iPlaceHolderText = NULL;

    iForceSyncPluginInst = ETrue;
    if (NW_Object_IsInstanceOf (iBox->parent, &NW_LMgr_ActiveContainerBox_Class))
        {
        NW_LMgr_ActiveContainerBox_SetActionType(iBox->parent, 
            NW_LMgr_ActionType_ActivateObject);
        }

    SyncPluginInstBounds(*deviceContext);
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::IsShowingPlaceHolder
//
// Returns ETrue if the place holder is being shown.
// -----------------------------------------------------------------------------
//
TBool CLMgrObjectBox::IsShowingPlaceHolder(void)
    {
    return iShowingPlaceHolder;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::IsActive
//
// Returns if the plugin is active or not.
// -----------------------------------------------------------------------------
//
TBool CLMgrObjectBox::IsActive() const
    {
    return iBoxListener->IsActive();
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::FindBoxTree
//
// Each CLMgrObjectBox is contained in a container-box.  In order to identify the 
// plugin box associated with a given elementNode the NW_CSS_Prop_elementId 
// CSS property is applied, where the property value equals the element-node.
// FindBoxTree returns the box that contains a NW_CSS_Prop_elementId with a
// value matching aBoxKey. 
// -----------------------------------------------------------------------------
//
NW_LMgr_Box_t* CLMgrObjectBox::FindBoxTree(const NW_LMgr_Box_t& aBoxTree,
        void* aBoxKey)
    {        
    NW_LMgr_BoxVisitor_t  boxVisitor;
    NW_LMgr_Box_t*        box = NULL;
    NW_LMgr_Box_t*        targetBox = NULL;
    
    NW_ASSERT(aBoxKey != NULL);

    NW_TRY (status)
        {
        // Init a box visitor
        status = NW_LMgr_BoxVisitor_Initialize(&boxVisitor, 
                CONST_CAST(NW_LMgr_Box_t*, &aBoxTree));
        NW_THROW_ON_ERROR(status);
    
        while ((box = NW_LMgr_BoxVisitor_NextBox(&boxVisitor, 0)) != NULL)
            {
            NW_LMgr_PropertyValueType_t type = NW_CSS_ValueType_Object;
            NW_LMgr_PropertyValue_t value;
        
            // Get the ElementId property
            status = NW_LMgr_Box_GetPropertyValue(box, NW_CSS_Prop_elementId, 
                    type, &value);

            if (status == KBrsrNotFound)
                {
                continue;
                }

            NW_THROW_ON_ERROR(status);

            // Does this box match the key?
            if (value.object == aBoxKey)
                {
                targetBox = box;
                break;
                }
            }
        }

    NW_CATCH (status)
        {
        targetBox = NULL;
        }

    NW_FINALLY 
        {
        return targetBox;
        }
    NW_END_TRY
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::FindObjectBox
//
// Each CLMgrObjectBox is contained in a container-box.  FindObjectBox finds 
// and returns the first NW_LMgr_ObjectBox found in aBoxTree.
// -----------------------------------------------------------------------------
//
NW_LMgr_Box_t* CLMgrObjectBox::FindObjectBox(const NW_LMgr_Box_t& aBoxTree)
    {        
	TBrowserStatusCode		status;
    NW_LMgr_BoxVisitor_t	boxVisitor;
    NW_LMgr_Box_t*			box = NULL;
    
    // Init a box visitor
    status = NW_LMgr_BoxVisitor_Initialize(&boxVisitor, 
            CONST_CAST(NW_LMgr_Box_t*, &aBoxTree));
	if (status != KBrsrSuccess)
	    {
		return NULL;
	    }

    while ((box = NW_LMgr_BoxVisitor_NextBox(&boxVisitor, 0)) != NULL)
        {
		if (NW_Object_IsInstanceOf(box, &NW_LMgr_ObjectBox_Class))
            {
			return box;
            }
        }

	return NULL;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::GetTextProperty
//
// Returns the given property as a descriptor.
// -----------------------------------------------------------------------------
//
TDesC* CLMgrObjectBox::GetTextProperty(const NW_LMgr_Box_t& aBox, 
        NW_LMgr_PropertyName_t aProperty)
    {
    TBrowserStatusCode  status;
    NW_LMgr_Property_t  prop;
    HBufC*              value = NULL;

    // Get the prop from the box
    prop.type = NW_CSS_ValueType_Text;
    prop.value.object = NULL;
    status = NW_LMgr_Box_GetProperty(CONST_CAST(NW_LMgr_Box_t*, &aBox), aProperty, &prop);
    
    if ((status == KBrsrSuccess) && (prop.value.object))
        {
        NW_Text_t*  temp;

        temp = (NW_Text_t*) prop.value.object;

        // Copy the result into a TDes.
        value = HBufC::New(temp->characterCount + 1);
        if (value)
            {
            value->Des().Append((const TUint16*) temp->storage, temp->characterCount);
            value->Des().ZeroTerminate();
            }
        }

    return value;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::GetParentWindow
//
// Returns the view infrastructure's CCoeControl associated the given root-box.
// -----------------------------------------------------------------------------
//
CCoeControl* CLMgrObjectBox::GetParentWindow(const NW_LMgr_RootBox_t& aRootBox)
    {
    MBoxTreeListener*  listener = NULL;
    CCoeControl*                 parentWindow = NULL;

    listener = NW_LMgr_RootBox_GetBoxTreeListener(CONST_CAST(NW_LMgr_RootBox_t*, &aRootBox));
    NW_ASSERT(listener != NULL);

    parentWindow = (CCoeControl*)listener->GetParentWindow();
    NW_ASSERT(parentWindow != NULL);

    return parentWindow;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::GetParentWindow
//
// Returns the view infrastructure's CCoeControl associated with this instance.
// -----------------------------------------------------------------------------
//
CCoeControl* CLMgrObjectBox::GetParentWindow(void)
    {
    NW_LMgr_RootBox_t* rootBox = NW_LMgr_Box_GetRootBox(iBox);

    if (rootBox)
        {
        return GetParentWindow(*rootBox);
        }
    else
        {
        return NULL;
        }
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::AttachPluginInst
//
// Shows, sizes, and locates the CPluginInst or place holder to correspond with 
// the box.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::AttachPluginInst(const NW_GDI_Point2D_s& aLocation,
        const NW_GDI_Dimension2D_t& aSize)
    {
    NW_GDI_Rectangle_t bounds;

    // If the place holder is showing determine if the place holder text needs
    // to be re-clipped.
    if (iShowingPlaceHolder)
        {
        // If the width of the box has changed the clipped place holder text may
        // need to be recalculated.
        if (aSize.width != iSize.width)
            {
            iCalculatePlaceHolderClip = ETrue;
            }
        }

    // Otherwise show the plugin in the correct location.
    else
        {
        if (!iPluginAttached)
            {
            iBoxListener->MakeVisible(ETrue);
            iPluginAttached = TRUE;
            }

        // Place it in the given location with the given size.
        bounds.point = aLocation;
        bounds.dimension = aSize;
    
        if ((bounds.dimension.height != 0) || (bounds.dimension.width != 0))
            {
            iBoxListener->SetExtent(bounds);
            }
        }

    // Remember the new location and size.
    iLocation = aLocation;
    iSize = aSize;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::DetachPluginInstL
//
// Detaches the CPluginInst from its parent.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::DetachPluginInst(void)
    {
    // Ignore this method if the place holder is showing
    if (iShowingPlaceHolder)
        {
        return;
        }

    // Otherwise hide the CPluginInst
    if (iPluginAttached && iBoxListener)
        {
        iBoxListener->MakeVisible(EFalse);

        // There is a funny Symbian bug that occurs if the BoxListener (a CCoeControl)
        // isn't FULLY contained within its parent control when the current page is
        // replaced.  To prevent this issue the control is moved to zero, zero when
        // the BoxListener is hidden.
        NW_GDI_Rectangle_t bounds;

        bounds.point.x = 0;
        bounds.point.y = 0;
        bounds.dimension = iSize;
        iBoxListener->SetExtent(bounds);
        }

        iPluginAttached = FALSE;
        iForceSyncPluginInst = ETrue;
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::SyncPluginInstBoundsL
//
// Adjusts the location and size of the PluginInst or place holder so it matches iBox's
// bounds.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::SyncPluginInstBounds(const CGDIDeviceContext& aDeviceContext)
    {
    NW_GDI_Rectangle_t  viewBounds;
    NW_GDI_Point2D_t    origin;
    NW_GDI_Rectangle_t  bounds;

    // Get the box's "inner" rect and the device origin
    (void) NW_LMgr_Box_GetInnerRectangle(iBox, &bounds);
    origin = *(aDeviceContext.Origin());

    // Get the view bounds.
    viewBounds = *(aDeviceContext.DisplayBounds());
    viewBounds.point = origin;

    // Detach the PluginInst if it isn't visible
    if (!NW_GDI_Rectangle_Cross(&bounds, &viewBounds, NULL))
        {
        iPluginAttached = ETrue;
        DetachPluginInst();
        }

    // Otherwise if necessary size and place the PluginInst
    else
        {
        // Determine the new location given the origin and bounds.point
        NW_GDI_Point2D_t  location;

        location.x = bounds.point.x - origin.x;
        location.y = bounds.point.y - origin.y;

        // If necessary adjust the location and size
        if ((iSize.width != bounds.dimension.width) ||
                (iSize.height != bounds.dimension.height) ||            
                (iLocation.x != location.x) || (iLocation.y != location.y) ||
                iForceSyncPluginInst)
            {
            AttachPluginInst(location, bounds.dimension);
            iForceSyncPluginInst = EFalse;
            }
        }
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::HandleVerticalLayout
//
// Implements the the Object handling rules for vertical layout
// 1. Scale the box maintaining the aspect ratio if width is greater than display width
// 2. Scale the box maintaining the aspect ratio if height is greater than display height
// 3. If box is too small it doesn't participate in the layout.
// -----------------------------------------------------------------------------
//
void CLMgrObjectBox::HandleVerticalLayout(const NW_GDI_Dimension2D_t& aOrigSize, 
        NW_GDI_Dimension2D_t& aNewSize)
    {
    CGDIDeviceContext*   deviceContext = NULL;
    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;
    NW_LMgr_RootBox_t*   rootBox = NULL;

    // Get the root box.
    rootBox = NW_LMgr_Box_GetRootBox(iBox);
    NW_ASSERT(rootBox != NULL);

    // Determine the maximum width and height for the box.
    deviceContext = NW_LMgr_RootBox_GetDeviceContext(rootBox);
    NW_ASSERT(deviceContext != NULL);

    rectangle = *(deviceContext->DisplayBounds());

    NW_LMgr_Box_GetPadding(iBox, &padding, ELMgrFrameAll );
    NW_LMgr_Box_GetBorderWidth(iBox, &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.height - padding.top - 
            padding.bottom - borderWidth.top - borderWidth.bottom);
    
    aNewSize = aOrigSize;

    // Scale the box maintaining the aspect ratio if height is greater than display height.
    if (aOrigSize.height > maxHeight)
        {
        aNewSize.width = (NW_GDI_Metric_t) ((maxHeight * aOrigSize.width) / aOrigSize.height);
        aNewSize.height = maxHeight;
        }

    // Scale the box maintaining the aspect ratio if width is greater than display width.
    if (aNewSize.width > maxWidth)
        {
        aNewSize.height = (NW_GDI_Metric_t) ((maxWidth * aNewSize.height) / aNewSize.width);
        aNewSize.width = maxWidth;
        }
    }

// -----------------------------------------------------------------------------
// CLMgrObjectBox::ClipText
//
// Creates a copy of aText such that it width is <= aAvailableSpace -- truncating 
// as necessary.
// -----------------------------------------------------------------------------
//
// TODO Share with IamgeContainerBox
TDesC* CLMgrObjectBox::ClipText(const TDesC& aText, const CGDIDeviceContext& aDeviceContext,
        NW_GDI_Metric_t aAvailableSpace, TBool aAddDots)
    {
    _LIT(kDots, "...");

    CGDIDeviceContext*      deviceContext = NULL;
    CGDIFont*               font = NULL;
    NW_GDI_FlowDirection_t  dir = NW_GDI_FlowDirection_LeftRight;
    NW_Text_UCS2_t          nwText;
    NW_Text_Length_t        clipLength;
    NW_Text_Length_t        newLength;
    HBufC*                  clippedText = NULL;
    TBool                   addDots = aAddDots;

    NW_TRY(status)
        {        
        // For convenience.
        deviceContext = CONST_CAST(CGDIDeviceContext*, &aDeviceContext);

        // Get the box font.
        font = NW_LMgr_Box_GetFont(iBox);
        NW_THROW_ON_NULL(font, status, KBrsrUnexpectedError);
        
        // Wrap aText in an NW_Text_t
        status = NW_Text_UCS2_Initialize(&nwText, (void*) aText.Ptr(), aText.Length(), 0);
        NW_THROW_ON_ERROR (status);

        // Clip the text
        status = deviceContext->SplitText((NW_Text_t*) &nwText, font, aAvailableSpace, 
                0, dir, &clipLength, NW_GDI_SplitMode_Clip);
        NW_THROW_ON_ERROR (status);
        
        // If the text fits in the space or if "..." aren't to be appended then set 
        // the copy-length to clipLength.
        if ((clipLength == NW_Text_GetCharCount(&nwText)) || (!aAddDots))
            {
            newLength = clipLength;
            addDots = EFalse;
            }

        // Otherwise split it again, but this time subtracting the length of "..."
        // from available space.
        else
            {
            NW_GDI_Dimension2D_t dotsExtent;

            // Wrap aText in an NW_Text_t
            status = NW_Text_UCS2_Initialize(&nwText, (void*) kDots().Ptr(), 
                    kDots().Length(), 0);
            NW_THROW_ON_ERROR (status);

            // Get the width of the dots
            dotsExtent.height = 0;
            dotsExtent.width = 0;

            status = deviceContext->GetTextExtent((NW_Text_t*)(&nwText), font,
                0, NW_GDI_FlowDirection_LeftRight, &dotsExtent);
            NW_THROW_ON_ERROR (status);

            // Wrap aText in an NW_Text_t
            status = NW_Text_UCS2_Initialize(&nwText, (void*) aText.Ptr(), aText.Length(), 0);
            NW_THROW_ON_ERROR (status);

            // Clip the text again this time less the dotExtent
            status = deviceContext->SplitText((NW_Text_t*) &nwText, font,
                    aAvailableSpace - dotsExtent.width, 0, dir, &clipLength, NW_GDI_SplitMode_Clip);
            NW_THROW_ON_ERROR (status);

            newLength = clipLength + kDots().Length();

            // CGDIDeviceContext::SplitText returns a clipLength of at least one
            // char.  Because of this a special case is needed.  This case occurs when
            // none of the text fits in aAvailableSpace and aAddDots is true.  In this
            // case the added dots may require more space than aAvailableSpace.
            if ((clipLength == 1) && (dotsExtent.width > aAvailableSpace))
                {
                newLength = 0;
                }
            }

        // Copy the cliped text
        if (newLength > 0)
            {
            clippedText = HBufC::New(newLength + 1);
            NW_THROW_OOM_ON_NULL(clippedText, status);

            clippedText->Des().Append(aText.Ptr(), clipLength);
        
            if (addDots)
                {
                clippedText->Des().Append(kDots().Ptr(), kDots().Length());
                }

            clippedText->Des().ZeroTerminate();
            }
        }

    NW_CATCH (status)
        {
        delete clippedText;
        clippedText = NULL;
        }

    NW_FINALLY
        {
        return clippedText;
        } 
    NW_END_TRY
    }

