javauis/eswt_akn/org.eclipse.ercp.swt.s60/native/src/swttree.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 09:48:13 +0300
branchRCL_3
changeset 24 6c158198356e
parent 21 4376525cdefb
permissions -rw-r--r--
Revision: v2.2.9 Kit: 201033

/*******************************************************************************
 * Copyright (c) 2005, 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Nokia Corporation - S60 implementation
 *******************************************************************************/


#include <aknsinglecolumnstyletreelist.h>
#include <AknsDrawUtils.h>
#include <AknUtils.h>
#include <avkon.mbg>
#include <barsread.h>
#include <swtlaffacade.h>
#include "eswtmobileextensions.h"
#include "swtfont.h"
#include "swttree.h"
#include "swtcontrolhelper.h"

#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
#include <touchfeedback.h>
#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK

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


CSwtTree* CSwtTree::NewL(MSwtDisplay& aDisplay, TSwtPeer aPeer,
                         MSwtComposite& aParent, TInt aStyle)
{
    CCoeControl& parentCtrl = aParent.Control()->CoeControl();
    CSwtTree* self = new(ELeave) CSwtTree(aDisplay, aPeer, aParent, aStyle,
                                          parentCtrl.IsVisible(), parentCtrl.IsDimmed());
    CleanupStack::PushL(self);
    self->ConstructL();
    self->InitControlBaseL();
    CleanupStack::Pop(self);
    return self;
}

CSwtTree::CSwtTree(MSwtDisplay& aDisplay, TSwtPeer aPeer,
                   MSwtComposite& aParent, TInt aStyle, TBool aVisibility, TBool aDimmed)
        : CSwtComposite(aDisplay, aPeer, &aParent, aStyle, aVisibility, aDimmed)
        , iItemHeightValid(EFalse)
        , iPointerRevertExpandNeeded(EFalse)
        , iPointerRevertCollapseNeeded(EFalse)
        , iRevertedItem(KAknTreeIIDNone)
{
}

void CSwtTree::ConstructL()
{
    CCoeControl& coeParent = iParent->Control()->CoeControl();
    SetContainerWindowL(coeParent);
    SetComponentsToInheritVisibility(ETrue);
    CCoeControl::MakeVisible(coeParent.IsVisible());
    CCoeControl::SetDimmed(coeParent.IsDimmed());

    iTree = CAknSingleColumnStyleTreeList::NewL(*this);
    TInt flags = KAknTreeListMarqueeScrolling;
    if (IsMarkable())
    {
        flags |= KAknTreeListMarkable;
    }
    iTree->SetFlags(flags);
    iTree->AddObserverL(this);

    // Eventhough container already given, we still need to do this for the skin background
    iTree->SetContainerWindowL(*this);

    // Give initial LAF value
    UpdateItemHeight();

    ActivateL();

    TDisplayMode displayMode(ENone);
    CEikonEnv* eikonEnv = iDisplay.CoeEnv();
    if (eikonEnv)
    {
        CWsScreenDevice* screenDevice = eikonEnv->ScreenDevice();
        if (screenDevice)
        {
            displayMode = screenDevice->DisplayMode();
        }
    }
    iMaskHandler = new CSwtMaskHandler(displayMode);
}

CSwtTree::~CSwtTree()
{
    delete iTree;
    iItems.Close();
    delete iMaskHandler;
    TInt count = iImages.Count();
    for (TInt i = 0; i < count; i++)
    {
        TSize imageSize = iImages[i]->Bitmap().SizeInPixels();
        if (imageSize.iHeight > iImageMaxSize.iHeight ||
                imageSize.iWidth > iImageMaxSize.iWidth)
        {
            // If image size is bigger than the old maximum size, then
            // the image has been also previously scaled.
            imageSize = SwtControlHelper::GetAspectRatioScaledBitmapSize(
                            imageSize, iImageMaxSize);
        }
        iImages[i]->RemoveSubRef(imageSize);
        iImages[i]->RemoveRef();
    }
    iImages.Close();
    iImageIds.Close();
    iImageRefs.Close();
}

TInt CSwtTree::CountComponentControls() const
{
    return 1;
}

CCoeControl* CSwtTree::ComponentControl(TInt /*aIdx*/) const
{
    return iTree;
}

void CSwtTree::HandleResourceChange(TInt aType)
{
    if (aType == KEikDynamicLayoutVariantSwitch)
    {
        iItemHeightValid = EFalse;
        UpdateItemHeight();
    }
    CAknControl::HandleResourceChange(aType);
    UpdateImagesSize();
}

TKeyResponse CSwtTree::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
{
    TBool traversalDoIt = ETrue;

    if (iItems.Count() > 0)
    {
        switch (aKeyEvent.iCode)
        {
        case EKeyEnter:
        case EKeyOK:
        {
            if (IsMarkable())
            {
                traversalDoIt = EFalse;
            }
            break;
        }
        case EKeyUpArrow:
        {
            traversalDoIt = EFalse;
            if (iTree->FocusedItem() == iItems[0])
            {
                if (GetShell().FindTraversalTargetL(ESwtTraverseArrowPrevious, *this))
                {
                    traversalDoIt = ETrue;
                }
            }
            break;
        }
        case EKeyDownArrow:
        {
            traversalDoIt = EFalse;
            if (iTree->FocusedItem() == LastCollapsedItem())
            {
                if (GetShell().FindTraversalTargetL(ESwtTraverseArrowNext, *this))
                {
                    traversalDoIt = ETrue;
                }
            }
            break;
        }
        case EKeyLeftArrow:
        {
            // Left key collapses node
            TInt item = iTree->FocusedItem();
            if (item != KAknTreeIIDNone)
            {
                traversalDoIt = EFalse;
                if (iTree->Parent(item) == KAknTreeIIDRoot && !iTree->IsExpanded(item))
                {
                    if (GetShell().FindTraversalTargetL(ESwtTraverseArrowPrevious, *this))
                    {
                        traversalDoIt = ETrue;
                    }
                }
            }
            break;
        }
        case EKeyRightArrow:
        {
            // Right key expands node
            TInt item = iTree->FocusedItem();
            if (item != KAknTreeIIDNone)
            {
                traversalDoIt = EFalse;
                if (iTree->ChildCount(item) == 0)
                {
                    if (GetShell().FindTraversalTargetL(ESwtTraverseArrowNext, *this))
                    {
                        traversalDoIt = ETrue;
                    }
                }
            }
            break;
        }
        default:
            break;
        }
    }

    return HandleKeyL(aKeyEvent, aType, traversalDoIt);
}

void CSwtTree::SizeChanged()
{
    if (iTree)
    {
        iTree->SetRect(BorderInnerRect());
    }
    CSwtComposite::SizeChanged();
}

void CSwtTree::PositionChanged()
{
    if (iTree)
    {
        iTree->SetRect(BorderInnerRect());
    }
    CSwtComposite::PositionChanged();
}

void CSwtTree::FocusChanged(TDrawNow aDrawNow)
{
    // This gets called before contained list is created.
    if (iTree)
    {
        TBool isFocused = IsFocusControl();
        iTree->SetFocus(isFocused, aDrawNow);
        if (isFocused)
        {
            UpdateItemHeight();
        }
    }
    HandleFocusChanged(aDrawNow);
}

CCoeControl& CSwtTree::CoeControl()
{
    return *this;
}

const CCoeControl& CSwtTree::CoeControl() const
{
    return *this;
}

// Needed for finding the skin background
TTypeUid::Ptr CSwtTree::MopSupplyObject(TTypeUid aId)
{
    TTypeUid::Ptr id = ASwtControlBase::SwtMopSupplyObject(aId);

    if (id.Pointer() == NULL)
    {
        return CAknControl::MopSupplyObject(aId);
    }
    else
    {
        return id;
    }
}

TBool CSwtTree::IsFocusable(TInt aReason /*=KSwtFocusByApi*/) const
{
    // Bypass CSwtComposite's focusability behavior.
    return ASwtScrollableBase::IsFocusable(aReason);
}

TInt CSwtTree::FocusBackgroundPolicy() const
{
#ifdef RD_JAVA_S60_RELEASE_9_2
    return ENoFocusBackgroundInCaptionedControl;
#else
    // Bypass CSwtComposite's focus background.
    return ASwtControlBase::FocusBackgroundPolicy();
#endif // RD_JAVA_S60_RELEASE_9_2
};

void CSwtTree::ProcessKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
{
    ASSERT(iTree);
    iTree->OfferKeyEventL(aKeyEvent, aType);
}

TBool CSwtTree::IsKeyUsed(TUint aKeyCode) const
{
    if (aKeyCode == EKeyBackspace)
    {
        return EFalse;
    }
    else if (aKeyCode == EKeyOK || aKeyCode == EKeyEnter)
    {
        if (!IsMarkable())
        {
            MSwtCommandArranger* commandArranger = iDisplay.CommandArranger();
            if (commandArranger)
            {
                if (commandArranger->IsContextSensitiveOperationSet())
                {
                    return EFalse;
                }
            }
            return ETrue;
        }
        else
        {
            return ETrue;
        }
    }
    else
    {
        return ETrue;
    }
}

TSize CSwtTree::ComputeSizeL(TInt aWHint, TInt aHHint)
{
    if (!iTree)
    {
        return TSize(0, 0);
    }

    TSize res(aWHint, aHHint);

    TInt count = iTree->ChildCount(KAknTreeIIDRoot);

    if (aWHint == KSwtDefault)
    {
        TRect mainRect(TRect::EUninitialized);
        AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainRect);
        TAknLayoutRect layoutRect = ItemLayoutRect();
        TRect rect = layoutRect.Rect();
        TInt vPadding = mainRect.Height() - rect.Height();
        TInt scrollBarAndTrimWidth = Max(0, mainRect.Width() - rect.Width());
        TAknLayoutText txtLayoutRect = CSwtLafFacade::GetLayoutText(CSwtLafFacade::EListSingle2HeadingMsgPaneT1,
                                       rect, IsMarkable() ? 3 : 2);
        TInt txtRightPadding = Max(0, rect.iBr.iX - txtLayoutRect.TextRect().iBr.iX);
        TInt txtLeftPadding = Max(0, txtLayoutRect.TextRect().iTl.iX);
        TInt markIconWidth(0);
        if (IsMarkable())
        {
            markIconWidth = CSwtLafFacade::GetLayoutRect(CSwtLafFacade::EListSingle2HeadingMsgPaneG2,
                            txtLayoutRect.TextRect(), 1).Rect().Width();
        }

        res.iWidth = 0;
        const CFont* font = txtLayoutRect.Font();
        if (font)
        {
            for (TInt i = 0; i < count; i++)
            {
                res.iWidth = Max(res.iWidth, font->TextWidthInPixels(iTree->Text(iTree->Child(KAknTreeIIDRoot, i))));
            }
        }

        res.iWidth += txtLeftPadding;
        res.iWidth += txtRightPadding;
        res.iWidth += scrollBarAndTrimWidth;
        res.iWidth += markIconWidth;
        res.iWidth += GetBorderWidth() * 2;
    }

    if (aHHint == KSwtDefault)
    {
        if (count == 0)
        {
            count = 1; // empty tree
        }
        res.iHeight = count * ItemHeight();
        res.iHeight += GetBorderWidth() * 2;
    }

    return res;
}

void CSwtTree::HandlePointerEventL(const TPointerEvent& aPointerEvent)
{
#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
    //Native control used for tree doesn't have flag for MULTI, so
    //given feedback is normal list feedback.
    //When tree is MULTI, we should give checkbox feedback on touch down.
    //So first we disable feedback for native side and do our
    //checkbox feedback. Then feedback for native side is restored.
    MTouchFeedback* feedback = NULL;
    if (aPointerEvent.iType == TPointerEvent::EButton1Up)
    {
        TUint32 flags = iTree->Flags();
        if ((flags & KAknTreeListMarkable) &&
                iTree->IsLeaf(iTree->FocusedItem()))
        {
            feedback = MTouchFeedback::Instance();
            if (feedback)
            {
                feedback->EnableFeedbackForControl(iTree, EFalse);
                feedback->InstantFeedback(ETouchFeedbackCheckbox);
            }
        }
    }
#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK

    iLastFocusedItem = iTree->FocusedItem();
    CSwtComposite::HandlePointerEventL(aPointerEvent);

    // Expanding node as part of pointer event reverting
    if (iPointerRevertExpandNeeded)
    {
        iTree->ExpandNode(iRevertedItem, ETrue);
        iPointerRevertExpandNeeded = EFalse;
    }

    // Collapsing node as part of pointer event reverting
    if (iPointerRevertCollapseNeeded)
    {
        iTree->CollapseNode(iRevertedItem, ETrue);
        iPointerRevertCollapseNeeded = EFalse;
    }

#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
    if (feedback)
    {
        //Restore native feedback.
        feedback->EnableFeedbackForControl(iTree, ETrue);
    }
#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK

    // The mouse event is posted to Java from CSwtComposite
}

MSwtComposite* CSwtTree::Composite()
{
    return this;
}

TInt CSwtTree::AddL(TInt aItemHandle)
{
    ASSERT(iTree);
    TInt flags = CAknSingleColumnStyleTreeList::EPersistent;
    if (aItemHandle < KAknTreeIIDRoot)
    {
        aItemHandle = KAknTreeIIDRoot;
    }

    TInt id = iTree->AddSubtitleRowL(aItemHandle, KNullDesC, flags, ETrue);

    // Ids of the children items are inserted after the id of the parent.
    // This is to facilitate 'insert with sort' mechanism.
    TBool parentFound = EFalse;
    if (aItemHandle != KAknTreeIIDRoot)
    {
        TInt index = iItems.Find(aItemHandle);
        if (index != KErrNotFound)
        {
            // Always at least +1 since item already added
            index += iTree->ChildCount(aItemHandle);
            User::LeaveIfError(iItems.Insert(id, index));
            parentFound = ETrue;
        }
    }

    if (!parentFound)
    {
        User::LeaveIfError(iItems.Append(id));
    }

    return id;
}

TRect CSwtTree::Bounds(TInt aItemHandle) const
{
    ASSERT(iTree);
    TRect rect(0, 0, 0, 0);
    TInt posInScreen = iTree->VisibleItemIndex(aItemHandle);
    if (posInScreen >= 0)
    {
        TInt itemHeight = ItemHeight();
        rect.iTl.iY = itemHeight * posInScreen;
        rect.SetWidth(iTree->Rect().Width());
        rect.SetHeight(itemHeight);
    }
    return rect;
}

void CSwtTree::Check(TInt aItemHandle, TBool aState)
{
    ASSERT(iTree);
    iTree->SetMarked(aItemHandle, aState, ETrue);
}

void CSwtTree::Collapse(TInt aItemHandle)
{
    ASSERT(iTree);
    if (iTree->ChildCount(aItemHandle) > 0)
    {
        iTree->CollapseNode(aItemHandle, ETrue);
    }
}

void CSwtTree::Expand(TInt aItemHandle)
{
    ASSERT(iTree);
    if (iTree->ChildCount(aItemHandle) > 0)
    {
        iTree->ExpandNode(aItemHandle, ETrue);
    }
}

TInt CSwtTree::InsertL(TInt aItemHandle, TInt aIndex)
{
    ASSERT(iTree);
    TInt flags = CAknSingleColumnStyleTreeList::EPersistent;
    if (aItemHandle < KAknTreeIIDRoot)
    {
        aItemHandle = KAknTreeIIDRoot;
    }

    TInt id = iTree->AddSubtitleRowL(aItemHandle, KNullDesC, flags, EFalse);

    // Insert id in the list ordered item id array
    if (KAknTreeIIDRoot == aItemHandle)
    {
        // Root item ids are inserted before the id position of aIndex sibbling
        TInt sibbling = iTree->Child(KAknTreeIIDRoot, aIndex);
        TInt index = iItems.Find(sibbling);
        if (index != KErrNotFound)
        {
            // Insert at sibbling's position
            ASSERT(index <= iItems.Count());
            User::LeaveIfError(iItems.Insert(id, index));
            iTree->Sort(this, ETrue);
        }
        else
        {
            // Insertion failure, just append the item id and pray
            User::LeaveIfError(iItems.Append(id));
        }
    }
    else
    {
        // Ids of the children items are inserted after the id of the parent.
        TInt index = iItems.Find(aItemHandle);
        if (index != KErrNotFound)
        {
            index++; // first position after parent
            index += aIndex;
            ASSERT(index <= iItems.Count());
            User::LeaveIfError(iItems.Insert(id, index));
            iTree->Sort(this, ETrue);
        }
        else
        {
            // Insertion failure, just append the item id and pray
            User::LeaveIfError(iItems.Append(id));
        }
    }

    return id;
}

TBool CSwtTree::IsExpanded(TInt aItemHandle) const
{
    ASSERT(iTree);
    if (iTree->ChildCount(aItemHandle) == 0)
    {
        return EFalse;
    }
    else
    {
        return iTree->IsExpanded(aItemHandle);
    }
}

TBool CSwtTree::IsSelected(TInt aItemHandle) const
{
    ASSERT(iTree);
    return iTree->IsMarked(aItemHandle);   // selected or checked
}

TInt CSwtTree::ItemAt(TInt aX, TInt aY) const
{
    TInt res = KErrNotFound;
    TRect rect = iTree->Rect();

    TPoint point(aX, aY);
    if (rect.Contains(point))
    {
        ASSERT(iTree);
        TInt count = iItems.Count();
        for (TInt i = 0; i < count; i++)
        {
            TInt id = iItems[i];
            if (Bounds(id).Contains(point))
            {
                return id;
            }
        }
    }

    return res;
}

TInt CSwtTree::ItemHeight() const
{
    const_cast<CSwtTree*>(this)->UpdateItemHeight();
    return iItemHeight;
}

void CSwtTree::Remove(TInt aItemHandle)
{
    ASSERT(iTree);

    // Must remove the item ids before removing the actual items.
    RemoveItemRefs(aItemHandle);

    // All children will automatically be removed too.
    iTree->RemoveItem(aItemHandle, ETrue);
}

void CSwtTree::RemoveAll()
{
    ASSERT(iTree);
    iItems.Reset();
    iTree->RemoveItem(KAknTreeIIDRoot, ETrue);
}

void CSwtTree::Select(const TInt* aItemHandles, TInt aCount, TBool aState)
{
    ASSERT(iTree);
    if (iStyle & KSwtStyleMulti)
    {
        for (TInt i = 0; i < aCount; i++)
        {
            iTree->SetMarked(aItemHandles[i], aState, ETrue);
        }
    }
    else
    {
        if (aState && aCount > 0)
        {
            iTree->SetFocusedItem(aItemHandles[0]);
        }
    }
}

void CSwtTree::SelectAll(TBool aState)
{
    ASSERT(iTree);
    iTree->SetMarked(KAknTreeIIDRoot, aState, ETrue);
}

TInt CSwtTree::SelectionCount() const
{
    const CArrayFix<TInt>* arr = NULL;
    TRAP_IGNORE(arr = SelectionL());
    TInt res(0);
    if (arr)
    {
        res = arr->Count();
    }
    delete arr;
    return res;
}

const CArrayFix<TInt>* CSwtTree::SelectionL() const
{
    ASSERT(iTree);
    CArrayFix<TInt>* res = NULL;
    if (iStyle & KSwtStyleMulti)
    {
        RArray<TInt> arr;
        CleanupClosePushL(arr);
        iTree->GetMarkedItemsL(arr);
        TInt count = arr.Count();
        if (count > 0)
        {
            res = new(ELeave) CArrayFixFlat<TInt>(count);
            CleanupStack::PushL(res);
            for (TInt i = 0; i < count; i++)
            {
                res->AppendL(arr[i]);
            }
            CleanupStack::Pop(res);
        }
        CleanupStack::PopAndDestroy(&arr);
    }
    else
    {
        TInt item = iTree->FocusedItem();
        if (item != KAknTreeIIDNone)
        {
            res = new(ELeave) CArrayFixFlat<TInt>(1);
            CleanupStack::PushL(res);
            res->AppendL(item);
            CleanupStack::Pop(res);
        }
    }
    return res;
}

// CSwtLafFacade::EListSingle2HeadingMsgPaneG1 could be used to implement downscaling
void CSwtTree::SetImageL(TInt aItemHandle, const MSwtImage* aImage)
{
    ASSERT(iTree);
    if (aImage)
    {
        TInt id = KErrNotFound;
        TInt index = iImages.Find(aImage);
        if (index != KErrNotFound)
        {
            // Image already used by other items, increase local ref count
            id = iImageIds[index];
            iImageRefs[index]++;
        }
        else
        {
            // best suitable size from layouts.
            TSize layoutImgSize = CSwtLafFacade::GetLayoutRect(CSwtLafFacade::EListSingle2HeadingMsgPaneG1,
                                  ItemLayoutRect().Rect(), 4).Rect().Size();
            // best image size we can make to fit in the above layout size.
            TSize bitmapSize = SwtControlHelper::GetAspectRatioScaledBitmapSize(
                                   aImage->Bitmap().SizeInPixels(), layoutImgSize);

            // Register image with CAknTree, increase MSwtImage ref count, store the new id for later use
            const CFbsBitmap& bmp = aImage->SubBitmap(bitmapSize);
            const CFbsBitmap* mask = aImage->SubMaskBitmap(bitmapSize, ETrue);
            aImage->AddSubRef(bitmapSize);
            iImageMaxSize = bitmapSize;
            if (!mask)
            {
                mask = iMaskHandler->GetMask(bmp.SizeInPixels());
            }
            id = iTree->AddIconL(const_cast<CFbsBitmap*>(&bmp), const_cast<CFbsBitmap*>(mask),
                                 EFalse, EAspectRatioPreserved);
            aImage->AddRef();
            User::LeaveIfError(iImages.Append(aImage));
            User::LeaveIfError(iImageIds.Append(id));
            User::LeaveIfError(iImageRefs.Append(1));
        }

        if (id != KErrNotFound)
        {
            // Set item custom image id
            iTree->SetIcon(aItemHandle, CAknSingleColumnStyleTreeList::ECollapsedNode, id, ETrue);
            iTree->SetIcon(aItemHandle, CAknSingleColumnStyleTreeList::EExpandedNode, id, ETrue);
        }
    }
    else
    {
        TInt id = iTree->Icon(aItemHandle, CAknSingleColumnStyleTreeList::EExpandedNode);
        if (id != KErrNotFound)
        {
            // Set default image id to item
            iTree->SetIcon(aItemHandle, CAknSingleColumnStyleTreeList::EExpandedNode,
                           AknTreeListIconID::KDefault, ETrue);
            iTree->SetIcon(aItemHandle, CAknSingleColumnStyleTreeList::ECollapsedNode,
                           AknTreeListIconID::KDefault, ETrue);
            TInt index = iImageIds.Find(id);
            if (index != KErrNotFound)
            {
                // Decrease local ref
                iImageRefs[index]--;
                if (iImageRefs[index] == 0)
                {
                    // No items are using the image, deregister it and decrease the MSwtImage ref count
                    iTree->RemoveIconL(id);
                    const MSwtImage* image = iImages[index];
                    TSize imageSize = image->Bitmap().SizeInPixels();
                    if (imageSize.iHeight > iImageMaxSize.iHeight ||
                            imageSize.iWidth > iImageMaxSize.iWidth)
                    {
                        imageSize = SwtControlHelper::GetAspectRatioScaledBitmapSize(
                                        imageSize, iImageMaxSize);
                    }
                    image->RemoveSubRef(imageSize);
                    image->RemoveRef();
                    iImages.Remove(index);
                    iImageIds.Remove(index);
                    iImageRefs.Remove(index);
                    iImages.Compress();
                    iImageIds.Compress();
                    iImageRefs.Compress();
                }
            }
        }
    }
}

void CSwtTree::SetTextL(TInt aItemHandle, const TDesC& aText)
{
    ASSERT(iTree);
    iTree->SetTextL(aItemHandle, aText, ETrue);
}

TInt CSwtTree::TopItem() const
{
    ASSERT(iTree);
    TInt count = iItems.Count();
    for (TInt i = 0; i < count; i++)
    {
        TInt id = iItems[i];
        if (iTree->VisibleItemIndex(id) == 0)
        {
            return id;
        }
    }
    return KErrNotFound;
}

TInt CSwtTree::HandleTreeListEvent(CAknTreeList& aList, TAknTreeItemID aItem,
                                   MAknTreeListObserver::TEvent aEvent)
{
    if (&aList != iTree)
    {
        return KErrNone;
    }

    // EItemSelected is never sent for nodes, hence the manual marking
    switch (aEvent)
    {
    case MAknTreeListObserver::ENodeExpanded:
    {
        /*
         * Pointer reverting has to be done after HandlePointerEvent. So here
         * just setting attributes and actual reverting is done in
         * CSwtTree::HandlePointerEventL
         * In case of reverting event, Java will not get any notifications.
         */
        if (iDisplay.RevertPointerEvent() && !iPointerRevertExpandNeeded)
        {
            iPointerRevertCollapseNeeded = ETrue;
            iRevertedItem = aItem;
        }
        else
        {
            TInt count = iTree->ChildCount(aItem);
            if (count > 0)
            {
                TRAP_IGNORE(iDisplay.PostTreeEventL(iPeer, ESwtEventExpand, aItem));
            }
            if (IsMarkable())
            {
                if (count == 0)
                {
                    iTree->SetMarked(aItem, ETrue, ETrue);
                }
            }
            else
            {
                TRAP_IGNORE(iDisplay.PostTreeEventL(iPeer, ESwtEventDefaultSelection, aItem));
            }
        }
        break;
    }
    case MAknTreeListObserver::ENodeCollapsed:
    {
        /*
         * Pointer reverting has to be done after HandlePointerEvent. So here
         * just setting attributes and actual reverting is done in
         * CSwtTree::HandlePointerEventL
         * In case of reverting event, Java will not get any notifications.
         */
        if (iDisplay.RevertPointerEvent() && !iPointerRevertCollapseNeeded)
        {
            iPointerRevertExpandNeeded = ETrue;
            iRevertedItem = aItem;
        }
        else
        {
            TInt count = iTree->ChildCount(aItem);
            if (count > 0)
            {
                TRAP_IGNORE(iDisplay.PostTreeEventL(iPeer, ESwtEventCollapse, aItem));
            }
            if (IsMarkable())
            {
                if (count == 0)
                {
                    iTree->SetMarked(aItem, EFalse, ETrue);
                }
            }
        }
        break;
    }
    case MAknTreeListObserver::EItemFocused:
    {
        if (iStyle & KSwtStyleSingle)
        {
            TRAP_IGNORE(iDisplay.PostTreeEventL(iPeer, ESwtEventSelection, aItem));
        }
        break;
    }
    case MAknTreeListObserver::EItemMarked:
    {
        TRAP_IGNORE(iDisplay.PostTreeEventL(iPeer, ESwtEventSelection, aItem));
        break;
    }
    case MAknTreeListObserver::EItemUnmarked:
    {
        TRAP_IGNORE(iDisplay.PostTreeEventL(iPeer, ESwtEventSelection, aItem));
        break;
    }
#ifdef RD_JAVA_S60_RELEASE_9_2
    case MAknTreeListObserver::EEventPanningStarted:
        GetShell().SetUrgentPaintControl(this);
        break;
    case MAknTreeListObserver::EEventFlickStopped:
        GetShell().SetUrgentPaintControl(NULL);
        break;
#endif // RD_JAVA_S60_RELEASE_9_2
    default:
        break;
    }

    return KErrNone;
}

TInt CSwtTree::Compare(TAknTreeItemID aFirst, TAknTreeItemID aSecond)
{
    TInt firstPos = iItems.Find(aFirst);
    TInt secondPos = iItems.Find(aSecond);
    if (firstPos < secondPos)
    {
        return -1;
    }
    else
    {
        return +1;
    }
}

void CSwtTree::UpdateItemHeight()
{
    if (iItemHeightValid)
    {
        return;
    }

    ASSERT(iTree);
    TRect rect = iTree->HighlightRect();
    if (!rect.IsEmpty())
    {
        iItemHeightValid = ETrue;
        iItemHeight = rect.Height();
        return;
    }

    // LAF as last resort, might be a few pixels inaccurate.
    iItemHeightValid = EFalse;
    iItemHeight = ItemLayoutRect().Rect().Height();
}

TAknLayoutRect CSwtTree::ItemLayoutRect()
{
    TRect mainRect(TRect::EUninitialized);
    AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainRect);
    TAknLayoutRect layoutRect = CSwtLafFacade::GetLayoutRect(CSwtLafFacade::EListScrollGenPane, mainRect, 0);
    layoutRect = CSwtLafFacade::GetLayoutRect(CSwtLafFacade::EListGenPane, layoutRect.Rect(), 0);
    layoutRect = CSwtLafFacade::GetLayoutRect(CSwtLafFacade::EListSingleGraphicH1Pane, layoutRect.Rect(), 0);
    return layoutRect;
}

void CSwtTree::UpdateImagesSize()
{
    TSize imageNewSize = CSwtLafFacade::GetLayoutRect(CSwtLafFacade::EListSingle2HeadingMsgPaneG1,
                         ItemLayoutRect().Rect(), 4).Rect().Size();

    if (imageNewSize != iImageMaxSize && iImages.Count() > 0)
    {
        TInt count = iItems.Count();
        for (TInt i = 0; i < count; i++)
        {
            TInt itemHandle = iItems[i];
            if (itemHandle)
            {
                // getting icon id from itemHandle.
                TInt id = iTree->Icon(itemHandle, CAknSingleColumnStyleTreeList::EExpandedNode);
                if (id != KErrNotFound)
                {
                    // image index from icon id.
                    TInt index = iImageIds.Find(id);
                    const MSwtImage* image = iImages[index];
                    TSize imageSize = image->Bitmap().SizeInPixels();

                    TSize oldSize;
                    TBool doScaling = ETrue;

                    if (imageSize.iHeight > iImageMaxSize.iHeight ||
                            imageSize.iWidth > iImageMaxSize.iWidth)
                    {
                        // If image size is bigger than the old maximum size, then
                        // the image has been also previously scaled.
                        oldSize = SwtControlHelper::GetAspectRatioScaledBitmapSize(
                                      imageSize, iImageMaxSize);
                    }
                    else if (imageSize.iHeight > imageNewSize.iHeight ||
                             imageSize.iWidth > imageNewSize.iWidth)
                    {
                        // Image is bigger than the new boundaries, but it did fit
                        // inside the old boundaries, so the image is default size.
                        oldSize = imageSize;
                    }
                    else
                    {
                        doScaling = EFalse;
                    }

                    if (doScaling)
                    {

                        // best image size to fit in the tree item.
                        imageSize = SwtControlHelper::GetAspectRatioScaledBitmapSize(
                                        imageSize, imageNewSize);
                        // scaling down the bitmap and mask.
                        const CFbsBitmap& bmp =  image->SubBitmap(imageSize);
                        const CFbsBitmap* mask =  image->SubMaskBitmap(imageSize, ETrue);

                        if (!mask)
                        {
                            mask = iMaskHandler->GetMask(bmp.SizeInPixels());
                        }
                        // replacing the old icon with new icon.
                        TRAP_IGNORE(iTree->AssignIconL(id, const_cast<CFbsBitmap*>(&bmp) ,
                                                       const_cast<CFbsBitmap*>(mask), EFalse, EAspectRatioPreserved));

                        image->AddSubRef(imageSize);
                        image->RemoveSubRef(oldSize);

                    }
                }
            }
        }
        iImageMaxSize = imageNewSize;
    }
}


TInt CSwtTree::LastCollapsedItem()
{
    TInt count = iItems.Count();
    if (count == 0)
    {
        return KErrNotFound;
    }
    TInt i;
    for (i = count - 1; i >= 0; i--)
    {
        TInt parentId = iTree->Parent(iItems[i]);
        if (parentId == KAknTreeIIDRoot)
        {
            break;
        }
        if (iTree->IsExpanded(parentId))
        {
            break;
        }
    }
    return iItems[i];
}

void CSwtTree::RemoveItemRefs(TInt aItemHandle)
{
    TInt index = iItems.Find(aItemHandle);
    if (index != KErrNotFound)
    {
        iItems.Remove(index);
    }
    TInt count = iTree->ChildCount(aItemHandle);
    for (int i = 0; i < count; i++)
    {
        RemoveItemRefs(iTree->Child(aItemHandle, i));
    }
}

TBool CSwtTree::IsMarkable() const
{
    // Natively, multi selection covers also the check style.
    // Java is responsible to not overselect in SINGLE & CHECK.
    return (iStyle & KSwtStyleMulti) || (iStyle & KSwtStyleCheck);
}

#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
void CSwtTree::DoControlSpecificFeedback(
    const TBool& aFirstTap,
    const TBool& aTappedToChildRect,
    const TPointerEvent& aPointerEvent) const
{
    MTouchFeedback* feedback = MTouchFeedback::Instance();
    if (feedback && !aTappedToChildRect)
    {
        switch (aPointerEvent.iType)
        {
        case TPointerEvent::EButton1Down:
            if (aFirstTap)
            {
                feedback->InstantFeedback(ETouchFeedbackSensitiveList);
            }
            break;
        }
    }
}
#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK