javauis/eswt_akn/org.eclipse.ercp.swt.s60/native/src/swtlink.cpp
branchRCL_3
changeset 14 04becd199f91
child 19 71c436fe3ce0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/eswt_akn/org.eclipse.ercp.swt.s60/native/src/swtlink.cpp	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,1767 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 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 <swtlaffacade.h>
+#include <swtbrowserschemehandler.h>
+#include <AknBidiTextUtils.h>
+#include <AknsUtils.h>
+#ifdef RD_TACTILE_FEEDBACK
+#include <touchfeedback.h>
+#endif // RD_TACTILE_FEEDBACK
+#include "swtfont.h"
+#include "swtlink.h"
+
+const TInt KBorderMargin = 1;
+
+/**
+ * Link fragment descriptor.
+ * Holds information about unformatted link text fragments.
+ */
+NONSHARABLE_CLASS(CSwtLinkFragmentDescriptor)
+        : public CBase
+{
+public:
+
+    static CSwtLinkFragmentDescriptor* NewL(TInt aOffset, TInt aLength);
+    static CSwtLinkFragmentDescriptor* NewL(TInt aOffset,
+                                            TInt aLength, const TDesC& aTarget);
+
+    TInt Offset() const;
+    TInt Length() const;
+    const TDesC* Target() const;
+
+    virtual ~CSwtLinkFragmentDescriptor();
+
+protected:
+    CSwtLinkFragmentDescriptor(TInt aOffset, TInt aLength);
+    void ConstructL(const TDesC& aTarget);
+
+private:
+    /**
+     * Offset from beginning of the string
+     */
+    TInt iOffset;
+    /**
+     * Length of the fragment in characters
+     */
+    TInt iLength;
+    /**
+     * Fragment target. Either string returned by selection listener,
+     * or NULL if fragment is non-active.
+     * Own.
+     */
+    HBufC* iTarget;
+};
+
+/**
+ * Drawable link fragment information container.
+ * Holds information about formatted fragments created for drawing.
+ */
+NONSHARABLE_CLASS(CSwtDrawableLinkFragment)
+        : public CBase
+{
+public:
+    static CSwtDrawableLinkFragment* NewL(
+        const TPtrC& aText,
+        const TRect& aRect,
+        const CFont& aFont,
+        const CSwtLinkFragmentDescriptor* aFragmentDescriptor);
+
+    virtual ~CSwtDrawableLinkFragment();
+
+protected:
+    CSwtDrawableLinkFragment(
+        const TRect& aRect,
+        const CSwtLinkFragmentDescriptor* aFragmentDescriptor);
+
+    void ConstructL(const TPtrC& aText, const CFont& aFont);
+
+public:
+    const TDes& Text() const;
+    const TRect& Rect() const;
+    const CSwtLinkFragmentDescriptor* FragmentDescriptor() const;
+
+private:
+    /**
+     * Rectangle into which fragment should be drawn.
+     *
+     */
+    TRect iRect;
+    /**
+     * Pointer to fragment descriptor to which this instance belongs.
+     * Not own.
+     */
+    const CSwtLinkFragmentDescriptor* iFragmentDescriptor;
+
+    /**
+     * Formatted text.
+     */
+    RBuf iFormattedText;
+};
+
+
+
+// ======== MEMBER FUNCTIONS ========
+
+/**
+ * Creates new instance of CSwtLink
+ */
+CSwtLink* CSwtLink::NewL(
+    MSwtDisplay&   aDisplay,
+    TSwtPeer       aPeer,
+    MSwtComposite& aParent,
+    TInt           aStyle)
+{
+    CSwtLink* self = new(ELeave) CSwtLink(aDisplay,
+                                          aPeer,
+                                          aParent,
+                                          aStyle);
+
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    self->InitControlBaseL();
+    CleanupStack::Pop(self);
+    return self;
+}
+
+/**
+ * Destructor
+ */
+CSwtLink::~CSwtLink()
+{
+#ifdef RD_TACTILE_FEEDBACK
+    if (iFeedback)
+    {
+        iFeedback->RemoveFeedbackForControl(this);
+    }
+#endif // RD_TACTILE_FEEDBACK
+    if (iDefaultFont)
+    {
+        iDefaultFont->RemoveRef();
+        iDefaultFont = NULL;
+    }
+    iText.Close();
+    iFormattedText.Close();
+    iDrawableLinkFragments.ResetAndDestroy();
+    iDrawableLinkFragments.Close();
+    iLinkFragmentsDescriptors.ResetAndDestroy();
+    iLinkFragmentsDescriptors.Close();
+}
+
+
+/**
+ * Constructor
+ */
+CSwtLink::CSwtLink(
+    MSwtDisplay& aDisplay,
+    TSwtPeer aPeer,
+    MSwtComposite& aParent,
+    TInt aStyle)
+        : ASwtControlBase(aDisplay, aPeer, &aParent, aStyle)
+        , iTextLineCount(0)
+        , iLineGap(0)
+        , iLineHeight(0)
+{
+    // Orientation needs to be coerced if it wasn't given explicitly
+    if (!(aStyle & KSwtOrientationMask))
+    {
+        iStyle &= ~KSwtOrientationMask;
+    }
+}
+
+
+/**
+ * Constructs current instnace
+ */
+void CSwtLink::ConstructL()
+{
+    CCoeControl& coeParent = iParent->Control()->CoeControl();
+
+    SetContainerWindowL(coeParent);
+    CAknControl::MakeVisible(coeParent.IsVisible());
+    CAknControl::SetDimmed(coeParent.IsDimmed());
+
+    UpdateDefaultFontL();
+    DoSetFont(&iDefaultFont->Font());
+    UpdateMarginValues();
+    UpdateSkinColor();
+    SetBackground(this);   // Back will be drawn by ASwtControlBase::Draw
+
+#ifdef RD_TACTILE_FEEDBACK
+    iFeedback = MTouchFeedback::Instance();
+#endif // RD_TACTILE_FEEDBACK
+
+    ActivateL();
+}
+
+/**
+ * Handles resource change
+ * @param aType Type of resource change.
+ */
+void CSwtLink::SwtHandleResourceChangeL(TInt aType)
+{
+    if (aType == KEikDynamicLayoutVariantSwitch)
+    {
+        UpdateDefaultFontL();
+        DoSetFont(&iDefaultFont->Font());
+        UpdateMarginValues();
+        BuildDrawableFragmentsListL(TextRect());
+    }
+    else if (aType == KAknsMessageSkinChange)
+    {
+        if (!iCustomTextColor)
+        {
+            UpdateSkinColor();
+        }
+    }
+}
+
+/**
+ * Updates skin colors
+ */
+void CSwtLink::UpdateSkinColor()
+{
+    AknsUtils::GetCachedColor(AknsUtils::SkinInstance(),
+                              iTextColor,
+                              KAknsIIDQsnTextColors,
+                              EAknsCIQsnTextColorsCG6);
+    AknsUtils::GetCachedColor(AknsUtils::SkinInstance(),
+                              iLinkColor,
+                              KAknsIIDQsnHighlightColors,
+                              EAknsCIQsnHighlightColorsCG3);
+    AknsUtils::GetCachedColor(AknsUtils::SkinInstance(),
+                              iHighlightColor,
+                              KAknsIIDQsnHighlightColors,
+                              EAknsCIQsnHighlightColorsCG2);
+    AknsUtils::GetCachedColor(AknsUtils::SkinInstance(),
+                              iHighlightedLinkColor,
+                              KAknsIIDQsnTextColors,
+                              EAknsCIQsnTextColorsCG24);
+}
+
+
+/**
+ * Updates default font
+ */
+void CSwtLink::UpdateDefaultFontL()
+{
+    if (iDefaultFont)
+    {
+        iDefaultFont->RemoveRef();
+        iDefaultFont = NULL;
+    }
+    iDefaultFont = CSwtFont::NewL(iDisplay.Device(),
+                                  iDisplay.Device().GetSystemFont()->Font());
+}
+
+/**
+ * Updates margins
+ */
+void CSwtLink::UpdateMarginValues()
+{
+    TMargins8 padding = iDisplay.UiUtils().InlineReferencePadding();
+    iHorizontalMargin = padding.iLeft;
+    iVerticalMargin = padding.iTop;
+}
+
+/**
+ * Builds list of fragment descriptors from array of text fragments and
+ * array of their targets.
+ *
+ * @param aTextFragments Array of text fragments
+ * @param aLinkTargets Array of strings which should be passed to
+ *                     selection listener.
+ * @returns Sum of all text fragments legths (ie. expected plain text
+ *          length).
+ *
+ * @remark When aLinkTargets item is empty string, the aTextFragments
+ *         item is considered to be non-active fragment.
+ *         So active fragments are only those which have non-empty
+ *         string in aLinkTargets array.
+ */
+TInt CSwtLink::BuildFragmentListL(
+    const CDesCArray* aTextFragments,
+    const CDesCArray* aLinkTargets)
+{
+    TInt actualTextLength = 0;
+    iLinkFragmentsDescriptors.ResetAndDestroy();
+    TInt fragmentCount = aTextFragments->Count();
+    TInt targetCount = 0;
+    for (TInt i = 0; i < fragmentCount; ++i)
+    {
+        if ((*aLinkTargets)[i].Length())
+        {
+            iLinkFragmentsDescriptors.AppendL(
+                CSwtLinkFragmentDescriptor::NewL(
+                    actualTextLength,
+                    (*aTextFragments)[i].Length(),
+                    (*aLinkTargets)[i]));
+            targetCount++;
+        }
+        else
+        {
+            iLinkFragmentsDescriptors.AppendL(
+                CSwtLinkFragmentDescriptor::NewL(
+                    actualTextLength,
+                    (*aTextFragments)[i].Length()));
+        }
+        actualTextLength += (*aTextFragments)[i].Length();
+    }
+
+    if (targetCount > 1)
+    {
+        iMultipleTargets = ETrue;
+    }
+    else
+    {
+        iMultipleTargets = EFalse;
+    }
+
+    return actualTextLength;
+}
+
+/**
+ * Assembles control's plain text from text fragments.
+ *
+ * @param aTextFragments Array of string which will be concatenated to form
+ *                      control's plain text.
+ * @param aTextLength Expected length of the resulting string.
+ */
+void CSwtLink::AssembleLinkTextL(
+    const CDesCArray* aTextFragments,
+    TInt aTextLength)
+{
+    iText.Close();
+    iText.CreateL(aTextLength);
+    TInt numberOfLines = 1;
+    for (TInt i = 0; i < aTextFragments->Count(); ++i)
+    {
+        TPtrC fragment = (*aTextFragments)[i];
+        iText.Append(fragment);
+        const TInt offset = fragment.Locate('\n');
+        if (KErrNotFound != offset)
+        {
+            numberOfLines++;
+        }
+    }
+    iTextLineCount = numberOfLines;
+}
+
+/**
+ * Builds drawable fragment list from control's text and given
+ * rectangle.
+ *
+ */
+void CSwtLink::BuildDrawableFragmentsListL(const TRect& aRect)
+{
+    iDrawableLinkFragments.ResetAndDestroy();
+    if (!iText.Length() || aRect.IsEmpty())
+    {
+        return;
+    }
+
+    const CFont& font(GetFont()->Font());
+    const CGraphicsContext::TTextAlign alignment = GetAligment();
+    const CFont::TMeasureTextInput::TFlags order = GetVisualOrder();
+
+    // Calculate the number of lines when wrapping text to the given rectangle
+    const TInt ARRAY_GRANULARITY = 4;
+    CArrayFixFlat<TPtrC>* linesArray =
+        new(ELeave) CArrayFixFlat<TPtrC>(ARRAY_GRANULARITY);
+    CleanupStack::PushL(linesArray);
+
+    //Wrap text to lines
+    WrapTextL(font, aRect, *linesArray);
+
+    //If there are no lines or no formatted text, return
+    if (!linesArray->Count() || !iFormattedText.Length())
+    {
+        return;
+    }
+
+    TInt currentLineIndex = 0;
+    TInt currentLinePosition = 0;
+    TInt currentTextPosition = 0;
+    //Get Y offset so the text is centered vertically in the control.
+    const TInt lineYOffset = GetLineYOffset(linesArray->Count(), aRect);
+    //Here we will map fragment descriptors to wrapped text and create
+    //drawable fragments which then will be drawn.
+    for (TInt fragmentIndex = 0;
+            fragmentIndex < iLinkFragmentsDescriptors.Count();
+            ++fragmentIndex)
+    {
+        //get length of the text in current fragment descriptor
+        TInt remainingFragmentLength =
+            iLinkFragmentsDescriptors[fragmentIndex]->Length();
+        while (remainingFragmentLength > 0 &&
+                currentLineIndex < linesArray->Count())
+        {
+            TPtrC currentLine = linesArray->At(currentLineIndex);
+            TInt remainingLineLength =
+                currentLine.Length() - currentLinePosition;
+
+            //Get width of the line in pixels and calculate X offset
+            //for current aligment
+            const TInt wholeLineWidth =
+                AknBidiTextUtils::MeasureTextBoundsWidth(font,
+                        currentLine, order);
+            const TInt lineXOffset = GetLineXOffset(alignment,
+                                                    wholeLineWidth, aRect.Width());
+
+            //Determine how many characters will be used from
+            //current text line
+            TInt charsConsumed = remainingFragmentLength;
+            if (remainingFragmentLength > remainingLineLength)
+            {
+                //Some chracters will be left for next text line
+                charsConsumed = remainingLineLength;
+            }
+
+            //Calculate position to which current portion of text
+            //should be drawn.
+            TRect currentRect = GetDrawableFragmentRectangle(font,
+                                currentLine, currentLinePosition,
+                                charsConsumed, aRect.Width(),
+                                lineXOffset, lineYOffset,
+                                currentLineIndex);
+
+            //Add new drawable fragment
+            iDrawableLinkFragments.AppendL(
+                CSwtDrawableLinkFragment::NewL(
+                    currentLine.Mid(currentLinePosition, charsConsumed),
+                    currentRect,
+                    font,
+                    iLinkFragmentsDescriptors[fragmentIndex]));
+
+            if (remainingFragmentLength > remainingLineLength)
+            {
+                //Move to the next line
+                ++currentLineIndex;
+                currentTextPosition += remainingLineLength;
+                currentLinePosition = 0;
+                //Wrapping text strips trailing spces at the end of each line
+                //Get correction for this.
+                TInt correction = DoTrailingWhitespaceCorrection(
+                                      currentTextPosition);
+                currentTextPosition += correction;
+                //Update number of characters remaining in current
+                //fragment descriptor
+                remainingFragmentLength -= charsConsumed + correction;
+            }
+            else
+            {
+                //Stay on current line
+                currentTextPosition += charsConsumed;
+                currentLinePosition += charsConsumed;
+                remainingLineLength -= charsConsumed;
+                remainingFragmentLength = 0;
+            }
+        }
+    }
+
+    linesArray->Reset();
+    CleanupStack::PopAndDestroy(linesArray);
+}
+
+/**
+ * Calculates rectangle for drawable fragment.
+ * @param aFont Font.
+ * @param aText Text to be drawn.
+ * @param aLinePosition Position in iFormattedText.
+ * @param aLength Length the text to be drawn.
+ * @param aAvailableWidth Width available for drawing.
+ * @param aXOffset X offset of the rectangle.
+ * @param aYOffset Y offset of the rectangle.
+ * @param aCurrentLineIndex Index of current line.
+ * @returns Rectnagle into which the drawable fragment should be drawn.
+ */
+TRect CSwtLink::GetDrawableFragmentRectangle(const CFont& aFont,
+        const TDesC& aText, const TInt aLinePosition,
+        const TInt aLength, const TInt aAvailableWidth,
+        const TInt aXOffset, const TInt aYOffset,
+        const TInt aCurrentLineIndex) const
+{
+    CFont::TMeasureTextInput::TFlags order = GetVisualOrder();
+
+    // Try to determine text directionality
+    TBool found = EFalse;
+    TInt directionality = TBidiText::TextDirectionality(aText, &found);
+    if (found)
+    {
+        switch (directionality)
+        {
+        case TBidiText::ERightToLeft:
+            order = CFont::TMeasureTextInput::EFVisualOrderRightToLeft;
+            break;
+        default:
+            order = CFont::TMeasureTextInput::EFVisualOrder;
+            break;
+        }
+    }
+
+    const TInt leftX = AknBidiTextUtils::MeasureTextBoundsWidth(
+                           aFont, aText.Left(aLinePosition), order);
+
+    const TInt rightX = AknBidiTextUtils::MeasureTextBoundsWidth(
+                            aFont, aText.Left(aLinePosition + aLength), order);
+
+    TInt tlX = 0;
+    TInt brX = 0;
+    if (IsRtl())
+    {
+        tlX = aAvailableWidth - rightX;
+        brX = aAvailableWidth - leftX;
+    }
+    else
+    {
+        tlX = aXOffset + leftX;
+        brX = aXOffset + rightX;
+    }
+
+    TRect currentRect(
+        tlX,
+        aYOffset + aCurrentLineIndex *(iLineHeight + iLineGap),
+        brX,
+        aYOffset + (aCurrentLineIndex + 1) *(iLineHeight + iLineGap));
+
+    return currentRect;
+}
+
+/**
+ * Returns number of white spaces from the given position to
+ * next non-white space character in formatted control's text.
+ *
+ * @param aCurrentTextPosition Current position in the formatted text
+ * @returns Number of white spaces between aCurrentTextPosition and next
+ *          non-white space character.
+ * @remark This method is used to coerce position in iText while
+ *          moving to the next line, because text wrapping strips
+ *          trailing line spaces in the lines.
+ */
+TInt CSwtLink::DoTrailingWhitespaceCorrection(
+    const TInt aCurrentTextPosition) const
+{
+    TInt correction = 0;
+    const TInt textLen = iText.Length();
+    if (aCurrentTextPosition < textLen)
+    {
+        TChar c(iText[aCurrentTextPosition]);
+        while (c.IsSpace()
+                && (aCurrentTextPosition + correction + 1) < textLen)
+        {
+            ++correction;
+
+            // Stop on EOL to prevent overcounting for leading spaces
+            if (c == '\n' || c == '\r')
+            {
+                break;
+            }
+
+            c = iText[aCurrentTextPosition + correction];
+        }
+    }
+
+    return correction;
+}
+
+/**
+ * Returns alignment depending on control's style and current layout
+ * settings.
+ */
+CGraphicsContext::TTextAlign CSwtLink::GetAligment() const
+{
+    CGraphicsContext::TTextAlign alignment;
+
+    if (iStyle & KSwtStyleCenter)
+    {
+        alignment = CGraphicsContext::ECenter;
+    }
+    else if (iStyle & KSwtStyleTrail)
+    {
+        if (IsRtl())
+        {
+            alignment = CGraphicsContext::ELeft;
+        }
+        else
+        {
+            alignment = CGraphicsContext::ERight;
+        }
+    }
+    else // default is left
+    {
+        if (IsRtl())
+        {
+            alignment = CGraphicsContext::ERight;
+        }
+        else
+        {
+            alignment = CGraphicsContext::ELeft;
+        }
+    }
+
+    return alignment;
+}
+
+/**
+ * Calculates X offset for the drawn line so the text is on the right
+ * position for given alignment.
+ * @param aAlignment Alignment of the line.
+ * @param aLineWidth Width of the text to be drawn in pixels.
+ * @param aAvailableWidth Width available for the line in pixels.
+ * @returns X offset
+ */
+TInt CSwtLink::GetLineXOffset(
+    const CGraphicsContext::TTextAlign aAlignment,
+    const TInt aLineWidth,
+    const TInt aAvailableWidth) const
+{
+    TInt offset = 0;
+    switch (aAlignment)
+    {
+    case CGraphicsContext::ELeft:
+        //Do nothing, 0 is already there
+        break;
+    case CGraphicsContext::ERight:
+        offset = aAvailableWidth - aLineWidth;
+        break;
+    case CGraphicsContext::ECenter:
+        offset = (aAvailableWidth - aLineWidth) / 2;
+        break;
+    default:
+        break;
+    }
+
+    return offset;
+}
+
+/**
+ * Calculates Y offset for the first drawn line so the
+ * text is vertically centered in the client area.
+ * @param aLineCount Number of lines to draw.
+ * @param aRect Rectangle into which text will be drawn.
+ * @returns Y offset
+ */
+TInt CSwtLink::GetLineYOffset(TInt aLineCount,
+                              const TRect& aRect) const
+{
+    const TInt offset = (aRect.Height()
+                         - aLineCount * (GetFont()->Font().FontMaxHeight() + iLineGap)
+                         + (aLineCount > 0 ? iLineGap : 0)) / 2;
+    return offset;
+}
+
+/**
+ * Returns current visual order
+ */
+CFont::TMeasureTextInput::TFlags CSwtLink::GetVisualOrder() const
+{
+    CFont::TMeasureTextInput::TFlags order =
+        CFont::TMeasureTextInput::EFVisualOrder;
+    if (IsRtl())
+    {
+        order = CFont::TMeasureTextInput::EFVisualOrderRightToLeft;
+    }
+
+    return order;
+}
+
+
+/**
+ * Wraps control's text to fit in given rectangle.
+ *
+ * @param aRect Target rectangle
+ * @param aWrappedArray Array of wrapped lines.
+ * @remark If there is not enough space for the text to fit in the
+ *          rectangle, last line is clipped.
+ *         This method updates iFormattedText to contain clipped text.
+ */
+void CSwtLink::WrapTextL(
+    const CFont& aFont,
+    const TRect& aRect,
+    CArrayFixFlat<TPtrC>& aWrappedArray)
+{
+    // The last row in label does not add a gap after it. So...
+    // aHHint = rows * textPaneHeight + (rows - 1) * gap; and therefore...
+    TInt rows = (aRect.Height()  + iLineGap) /
+                (aFont.FontMaxHeight() + iLineGap);
+    if (rows < 1)
+    {
+        // It happens when hint height < Char's height
+        // here just set nBStrings to 1
+        // so something can be visible.
+        rows = 1;
+    }
+
+    // Setup width arrray
+    CArrayFixFlat< TInt >* widthsArray =
+        new(ELeave) CArrayFixFlat< TInt >(rows);
+    CleanupStack::PushL(widthsArray);
+    widthsArray->SetReserveL(rows);
+    TInt i;
+    for (i = 0; i < rows; ++i)
+    {
+        widthsArray->AppendL(aRect.Width());
+    }
+
+    // If formatted text doesn't have enough space we need to
+    // resize it.
+    if (iFormattedText.Length() < rows)
+    {
+        iFormattedText.Close();
+        iFormattedText.CreateL(iText.Length() + rows);
+    }
+
+    // AknTextUtils::WrapToArrayAndClipL can change the string when
+    // adding ellipsis, therefore we work with copy here.
+    iFormattedText.Copy(iText);
+
+    AknTextUtils::WrapToArrayAndClipL(iFormattedText, *widthsArray,
+                                      aFont, aWrappedArray);
+    widthsArray->Reset();
+    CleanupStack::PopAndDestroy(widthsArray);
+}
+
+/**
+ * Returns rectangle into which text should be drawn.
+ */
+TRect CSwtLink::TextRect() const
+{
+    TRect textRect = Rect();
+    textRect.Shrink(iHorizontalMargin + KBorderMargin,
+                    iVerticalMargin + KBorderMargin);
+    return textRect;
+}
+
+/**
+ * Finds fragment descriptor at given position.
+ *
+ * @param aPoint Point relative to control.
+ * @returns Fragment descriptor drawn under aPoint.
+ * If there is no fragment desriptor under the point,
+ *      returns NULL.
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FindFragmentOnPosition(
+    const TPoint& aPoint) const
+{
+    const CSwtLinkFragmentDescriptor* fragment = NULL;
+    const TPoint offsetPoint(aPoint - TextRect().iTl);
+    for (TInt i = 0; i < iDrawableLinkFragments.Count(); ++i)
+    {
+        CSwtDrawableLinkFragment* currentDrawableFragment =
+            iDrawableLinkFragments[i];
+        if (currentDrawableFragment->Rect().Contains(offsetPoint))
+        {
+            fragment = iDrawableLinkFragments[i]->FragmentDescriptor();
+            break;
+        }
+    }
+    return fragment;
+}
+
+/**
+ * Finds first active fragment descriptor and verifies it is drawn.
+ *
+ * @returns First active fragment descriptor.
+ * If there are no active fragments,
+ *      returns NULL.
+ * If first fragment descriptor is found, but it is not drawn,
+ *      returns NULL.
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FindFirstActiveFragment() const
+{
+    return FindNextActiveFragment(NULL);
+}
+
+/**
+ * Finds next active fragment descriptor and verifies it is drawn.
+ *
+ * @param aFragmentDescriptor Current fragment descriptor
+ * @returns Next active fragment descriptor.
+ * If aFragmentDescriptor is NULL,
+ *      returns first active fragment descriptor.
+ * If aFragmentDescriptor points to the last active fragment descriptor,
+ *      returns NULL.
+ * If next fragment descriptor is found, but it is not drawn,
+ *      returns NULL.
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FindNextActiveFragment(
+    const CSwtLinkFragmentDescriptor* aFragmentDescriptor) const
+{
+    return FindActiveFragment(aFragmentDescriptor,
+                              0, iLinkFragmentsDescriptors.Count() - 1);
+}
+
+/**
+ * Finds last active fragment descriptor and verifies it is drawn.
+ *
+ * @returns Last active fragment descriptor.
+ * If there are no active fragments,
+ *      returns NULL.
+ * If last fragment descriptor is found, but it is not drawn,
+ *      returns NULL.
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FindLastActiveFragment() const
+{
+    return FindPreviousActiveFragment(NULL);
+}
+
+/**
+ * Finds previous active fragment descriptor and verifies it is drawn.
+ *
+ * @param aFragmentDescriptor Current fragment descriptor
+ * @returns Previous active fragment descriptor.
+ * If aFragmentDescriptor is NULL,
+ *      returns last active fragment descriptor.
+ * If aFragmentDescriptor points to first active fragment descriptor,
+ *      returns NULL.
+ * If previous fragment descriptor is found, but it is not drawn,
+ *      returns NULL.
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FindPreviousActiveFragment(
+    const CSwtLinkFragmentDescriptor* aFragmentDescriptor) const
+{
+    return FindActiveFragment(aFragmentDescriptor,
+                              iLinkFragmentsDescriptors.Count() - 1, 0);
+}
+
+/**
+ * Finds active fragment next to given fragment in forward or
+ * backward order depending on given aFrom an aTo values.
+ * @param aFragmentDescriptor Current fragment descriptor
+ * @param aFrom Current fragment descriptor
+ * @param aTo Current fragment descriptor
+ * @returns Fragment descriptor next to the aFragmentDescriptor in
+ *          direction defined by aFrom and aTo
+ * If aFragmentDescriptor is NULL,
+ *      returns first active fragment descriptor found.
+ * If aFragmentDescriptor points to first active fragment descriptor,
+ *      returns NULL.
+ * If previous fragment descriptor is found, but it is not drawn,
+ *      returns NULL.
+ *
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FindActiveFragment(
+    const CSwtLinkFragmentDescriptor* aFragmentDescriptor,
+    const TInt aFrom,
+    const TInt aTo) const
+{
+    if (aFrom < 0 || aTo < 0)
+    {
+        return NULL;
+    }
+
+    TBool currentFound = NULL == aFragmentDescriptor;
+    const CSwtLinkFragmentDescriptor* fragment = NULL;
+    const TInt change = aFrom < aTo ? 1 : -1;
+    const TBool upDownDirection = aFrom < aTo;
+    for (TInt i = aFrom; (upDownDirection && i <= aTo)
+            || (!upDownDirection && i >= aTo); i += change)
+    {
+        if (currentFound)
+        {
+            if (iLinkFragmentsDescriptors[i]->Target())
+            {
+                fragment = iLinkFragmentsDescriptors[i];
+                break;
+            }
+        }
+        else if (iLinkFragmentsDescriptors[i] == aFragmentDescriptor)
+        {
+            currentFound = ETrue;
+        }
+    }
+    return FragmentDescOrNullIfNotVisible(fragment);
+}
+
+
+/**
+ * Verifies that particular fragment descriptor is displayed.
+ * @param aFragmentDescriptor Fragment decriptor to be verified.
+ * @returns aFragmentDescriptor if at least part of it is drawn;
+ *          NULL otherwise.
+ */
+const CSwtLinkFragmentDescriptor* CSwtLink::FragmentDescOrNullIfNotVisible(
+    const CSwtLinkFragmentDescriptor* aFragmentDescriptor) const
+{
+    const CSwtLinkFragmentDescriptor* fragment = NULL;
+
+    for (TInt fragmentIndex = 0;
+            fragmentIndex < iDrawableLinkFragments.Count();
+            ++fragmentIndex)
+    {
+        if (iDrawableLinkFragments[fragmentIndex]
+                ->FragmentDescriptor() == aFragmentDescriptor)
+        {
+            fragment = aFragmentDescriptor;
+            break;
+        }
+    }
+
+    return fragment;
+}
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+TKeyResponse CSwtLink::OfferKeyEventL(const TKeyEvent& aKeyEvent,
+                                      TEventCode       aType)
+{
+    TBool traversalDoIt(ETrue);
+    if (aKeyEvent.iCode == EKeyOK || aKeyEvent.iCode == EKeyEnter)
+    {
+        traversalDoIt = EFalse;
+    }
+    else if (aKeyEvent.iCode == EKeyLeftArrow)
+    {
+        iFocusedFragment = FindPreviousActiveFragment(iFocusedFragment);
+        traversalDoIt = (NULL == iFocusedFragment) &&
+                        GetShell().FindTraversalTargetL(ESwtTraverseArrowNext, *this);
+    }
+    else if (aKeyEvent.iCode == EKeyRightArrow)
+    {
+        iFocusedFragment = FindNextActiveFragment(iFocusedFragment);
+        traversalDoIt = (NULL == iFocusedFragment)  &&
+                        GetShell().FindTraversalTargetL(ESwtTraverseArrowNext, *this);
+    }
+    return HandleKeyL(aKeyEvent, aType, traversalDoIt);
+}
+
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::HandleResourceChange(TInt aType)
+{
+    CAknControl::HandleResourceChange(aType);
+    TRAP_IGNORE(SwtHandleResourceChangeL(aType));
+}
+
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+TTypeUid::Ptr CSwtLink::MopSupplyObject(TTypeUid aId)
+{
+    return ASwtControlBase::SwtMopSupplyObject(aId);
+}
+
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::SizeChanged()
+{
+    TRAP_IGNORE(BuildDrawableFragmentsListL(TextRect()));
+    SetFocusedFragment();
+    HandleSizeChanged();
+}
+
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::PositionChanged()
+{
+    HandlePositionChanged();
+}
+
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::FocusChanged(TDrawNow aDrawNow)
+{
+    SetFocusedFragment();
+    HandleFocusChanged(aDrawNow);
+}
+
+
+// ---------------------------------------------------------------------------
+// From class CCoeControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::Draw(const TRect& /*aRect*/) const
+{
+    CWindowGc& gc = SystemGc();
+    const CFont* font(&GetFont()->Font());
+    const TRect textRect(TextRect());
+    for (TInt i = 0; i < iDrawableLinkFragments.Count(); ++i)
+    {
+        CSwtDrawableLinkFragment* fragment = iDrawableLinkFragments[i];
+        TRect rect = fragment->Rect();
+        rect.Move(textRect.iTl);
+
+        TRgb textColor = iTextColor;
+        if (fragment->FragmentDescriptor()->Target())
+        {
+            textColor = iLinkColor;
+        }
+
+        // Same background highlight as that of HyperLink
+        if (fragment->FragmentDescriptor() == iFocusedFragment
+                && (iPressed || iDisplay.UiUtils().NaviKeyInput()))
+        {
+            textColor = iHighlightedLinkColor;
+            gc.SetPenStyle(CGraphicsContext::ENullPen);
+            gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
+            gc.SetBrushColor(iHighlightColor);
+            gc.DrawRect(rect);
+        }
+
+        gc.UseFont(font);
+        gc.SetPenStyle(CGraphicsContext::ESolidPen);
+        gc.SetBrushStyle(CGraphicsContext::ENullBrush);
+        gc.SetUnderlineStyle(fragment->FragmentDescriptor()->Target()
+                             ? EUnderlineOn : EUnderlineOff);
+        gc.SetPenColor(textColor);
+
+        const TInt baseLineY = font->FontMaxAscent();
+        TPoint drawPos(rect.iTl.iX, rect.iTl.iY + baseLineY);
+        gc.DrawText(fragment->Text(), drawPos);
+    }
+}
+
+
+// ---------------------------------------------------------------------------
+// From class MSwtControl.
+// ---------------------------------------------------------------------------
+//
+CCoeControl& CSwtLink::CoeControl()
+{
+    return *this;
+}
+
+
+// ---------------------------------------------------------------------------
+// From class MSwtControl.
+// ---------------------------------------------------------------------------
+//
+const CCoeControl& CSwtLink::CoeControl() const
+{
+    return *this;
+}
+
+
+// ---------------------------------------------------------------------------
+// From class MSwtControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::ProcessKeyEventL(
+    const TKeyEvent& aKeyEvent,
+    TEventCode aType)
+{
+    if (aType == EEventKey)
+    {
+        if ((aKeyEvent.iCode == EKeyOK || aKeyEvent.iCode == EKeyEnter)
+                && NULL != iFocusedFragment)
+        {
+            iDisplay.PostSelectionEventL(iPeer, *iFocusedFragment->Target());
+        }
+        else if (aKeyEvent.iCode == EKeyLeftArrow
+                 || aKeyEvent.iCode == EKeyRightArrow)
+        {
+            Redraw();
+        }
+    }
+}
+
+
+// ---------------------------------------------------------------------------
+// From class MSwtControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::HandlePointerEventL(const TPointerEvent& aPointerEvent)
+{
+    const CSwtLinkFragmentDescriptor* tappedFragment = NULL;
+    switch (aPointerEvent.iType)
+    {
+    case TPointerEvent::EButton1Down:
+    {
+        if (iMultipleTargets)
+        {
+            iFocusedFragment = NULL;
+            tappedFragment = FindFragmentOnPosition(aPointerEvent.iPosition);
+        }
+        else
+        {
+            // If single target, the focused fragment does not change
+            // with pointer
+            tappedFragment = iFocusedFragment;
+        }
+        if (tappedFragment && tappedFragment->Target())
+        {
+            iPressed = ETrue;
+            iFocusedFragment = tappedFragment;
+#ifdef RD_TACTILE_FEEDBACK
+            if (iFeedback)
+            {
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+                iFeedback->InstantFeedback(ETouchFeedbackSensitiveButton);
+#else
+                iFeedback->InstantFeedback(ETouchFeedbackBasic);
+#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+            }
+#endif //RD_TACTILE_FEEDBACK
+        }
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+        else if (iFocusChanged)
+        {
+            iFeedback->InstantFeedback(ETouchFeedbackSensitiveList);
+            iFocusChanged = EFalse;
+        }
+#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+
+        if (iPressed)
+        {
+            Redraw();
+        }
+        break;
+    }
+
+    case TPointerEvent::EDrag:
+    {
+        TBool pressed = iPressed;
+
+        if (iMultipleTargets)
+        {
+            tappedFragment = FindFragmentOnPosition(aPointerEvent.iPosition);
+        }
+        else
+        {
+            if (Rect().Contains(aPointerEvent.iPosition))
+            {
+                // If single target, the focused fragment does not change
+                // with pointer
+                tappedFragment = iFocusedFragment;
+            }
+        }
+
+        if (tappedFragment && tappedFragment == iFocusedFragment)
+        {
+            iPressed = ETrue;
+        }
+        else
+        {
+            iPressed = EFalse;
+        }
+
+        if (pressed != iPressed)
+        {
+            Redraw();
+        }
+        break;
+    }
+
+    case TPointerEvent::EButton1Up:
+    {
+        TBool pressed = iPressed;
+        iPressed = EFalse;
+
+        if (iMultipleTargets)
+        {
+            tappedFragment = FindFragmentOnPosition(aPointerEvent.iPosition);
+        }
+        else
+        {
+            if (Rect().Contains(aPointerEvent.iPosition))
+            {
+                // If single target, the focused fragment does not change with pointer
+                tappedFragment = iFocusedFragment;
+            }
+        }
+
+        if (!iDisplay.RevertPointerEvent() && tappedFragment
+                && tappedFragment == iFocusedFragment && tappedFragment->Target())
+        {
+            iDisplay.PostSelectionEventL(iPeer, *iFocusedFragment->Target());
+        }
+
+        if (pressed != iPressed)
+        {
+            Redraw();
+        }
+        break;
+    }
+    }
+}
+
+// ---------------------------------------------------------------------------
+// From class MSwtControl.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::SetForegroundL(const MSwtColor* aColor)
+{
+    ASwtControlBase::DoSetForegroundL(aColor);
+    aColor ? iCustomTextColor = ETrue : iCustomTextColor = EFalse;
+    if (iCustomTextColor)
+    {
+        TRgb rgb;
+        TBool overrideColorSet(GetColor(EColorControlText, rgb));
+        ASSERT(overrideColorSet);
+        iTextColor = rgb;
+        iLinkColor = rgb;
+    }
+    else
+    {
+        UpdateSkinColor();
+    }
+    Redraw();
+}
+
+
+// ---------------------------------------------------------------------------
+// From class MSwtControl.
+// ---------------------------------------------------------------------------
+//
+TSize CSwtLink::ComputeSizeL(TInt aWHint, TInt aHHint)
+{
+    if (aWHint != KSwtDefault && aHHint != KSwtDefault)
+    {
+        return TSize(aWHint, aHHint);
+    }
+
+    return ComputeTextSizeL(aWHint, aHHint);
+}
+
+
+// ---------------------------------------------------------------------------
+// CSwtLink::ComputeTextSizeL
+// ---------------------------------------------------------------------------
+//
+TSize CSwtLink::ComputeTextSizeL(TInt aWHint, TInt aHHint)
+{
+    // Return non default hints
+    if (aWHint != KSwtDefault && aHHint != KSwtDefault)
+    {
+        return TSize(aWHint, aHHint);
+    }
+
+    // Compute size
+    TSize prefSize(TSize::EUninitialized);
+    const TInt vertMargin = 2 * (iVerticalMargin + KBorderMargin);
+    const TInt horizMargin = 2 * (iHorizontalMargin + KBorderMargin);
+    if (!(aWHint == KSwtDefault && aHHint == KSwtDefault))
+    {
+        // Wrap text with a default hint
+        if (aWHint != KSwtDefault)
+        {
+            prefSize.iWidth = aWHint;
+            prefSize.iHeight = CalcWrappedTextHeightL(aWHint - horizMargin)
+                               + vertMargin;
+        }
+        else
+        {
+            prefSize.iHeight = aHHint;
+            prefSize.iWidth = CalcWrappedTextWidth(aHHint - vertMargin)
+                              + horizMargin;
+        }
+    }
+    else
+    {
+        // This will return the width of the longest line and height that
+        // fits all lines
+        prefSize = MinimumSize();
+        prefSize += TPoint(horizMargin, vertMargin);
+    }
+
+    if (aHHint != KSwtDefault)
+    {
+        prefSize.iHeight = aHHint;
+    }
+
+    if (aWHint != KSwtDefault)
+    {
+        prefSize.iWidth = aWHint;
+    }
+
+    return prefSize;
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::CalcWrappedTextHeightL
+// ---------------------------------------------------------------------------
+//
+TInt CSwtLink::CalcWrappedTextHeightL(TInt aWidth)
+{
+    if (iText.Length() == 0 || aWidth <= 0)
+    {
+        return 0;
+    }
+
+    // Calculate the number of lines when wrapping text to the given rectangle
+    const TInt ARRAY_GRANULARITY = 4;
+    CArrayFixFlat<TPtrC>* wrappedArray = new(ELeave) CArrayFixFlat<TPtrC>(
+        ARRAY_GRANULARITY);
+    CleanupStack::PushL(wrappedArray);
+    const CFont* font(&GetFont()->Font());
+
+    AknTextUtils::WrapToArrayL(iText, aWidth, *font, *wrappedArray);
+
+    TInt nbStrings = wrappedArray->Count();
+    if (nbStrings < 1)
+    {
+        // It happens when aWidth < Char's width
+        // here just set nBStrings to 1
+        // so something can be visible.
+        nbStrings = 1;
+    }
+
+    // Calculate text height
+    const TInt result(MinimumHeight(nbStrings));
+
+    wrappedArray->Reset();
+    CleanupStack::PopAndDestroy(wrappedArray);
+    return result;
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::CalcWrappedTextWidth
+// ---------------------------------------------------------------------------
+//
+TInt CSwtLink::CalcWrappedTextWidth(TInt aHeight)
+{
+    if (iText.Length() == 0 || aHeight <= 0)
+    {
+        return 0;
+    }
+    // This will return the width of the longest line and height that fits
+    // all lines
+    return MinimumWidth();
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::MinimumSize
+// ---------------------------------------------------------------------------
+//
+TSize CSwtLink::MinimumSize() const
+{
+    const TInt height = MinimumHeight();
+    const TInt width = MinimumWidth();
+    return TSize(width, height);
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::MinimumHeight
+// ---------------------------------------------------------------------------
+//
+TInt CSwtLink::MinimumHeight() const
+{
+    return(MinimumHeight(iTextLineCount));
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::MinimumHeight
+// ---------------------------------------------------------------------------
+//
+TInt CSwtLink::MinimumHeight(TInt aLineCount) const
+{
+    const MSwtFont* font = GetFont();
+    TInt res = aLineCount *
+               (iLineGap + font->Font().FontMaxHeight())
+               - (aLineCount > 0 ? iLineGap : 0);
+
+    // Help the inline case. Makes sense if there is only one line of text
+    if (aLineCount == 1 && font == &DefaultFont())
+    {
+        res = Max(iDisplay.UiUtils().InlineReferenceFontHeight(), res);
+    }
+
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::MinimumWidth
+// ---------------------------------------------------------------------------
+//
+TInt CSwtLink::MinimumWidth() const
+{
+    if (iText.Length() == 0)
+    {
+        return 0;
+    }
+
+    TInt result = 0;
+    TPtrC ptr(iText);
+    const CFont* font(&GetFont()->Font());
+    CFont::TMeasureTextInput::TFlags order =
+        CFont::TMeasureTextInput::EFVisualOrder;
+    if (IsRtl())
+    {
+        order = CFont::TMeasureTextInput::EFVisualOrderRightToLeft;
+    }
+
+    FOREVER
+    {
+        const TInt pos = ptr.Locate('\n');
+        TPtrC left = ptr;
+        if (pos >= 0)
+        {
+            left.Set(ptr.Left(pos));
+        }
+        TInt lineW = AknBidiTextUtils::MeasureTextBoundsWidth(*font, left,
+                     order);
+        if (lineW > result)
+        {
+            result = lineW;
+        }
+        if (pos < 0 || pos == ptr.Length() - 1)
+        {
+            break;
+        }
+        ptr.Set(ptr.Mid(pos + 1));
+    }
+
+    return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// CSwtLink::IsRtl
+// ---------------------------------------------------------------------------
+//
+TBool CSwtLink::IsRtl() const
+{
+    TBool result = AknLayoutUtils::LayoutMirrored();
+    if (iStyle & KSwtStyleLeftToRight)
+    {
+        result = EFalse;
+    }
+    else if (iStyle & KSwtStyleRightToLeft)
+    {
+        result = ETrue;
+    }
+    return result;
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLink::DefaultFont
+// From MSwtControl
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::SetFontL(const MSwtFont* aFont)
+{
+    ASwtControlBase::DoSetFontL(aFont);
+    DoSetFont(&GetFont()->Font());
+    Redraw();
+}
+
+
+void CSwtLink::DoSetFont(const CFont* aFont)
+{
+    iLineGap = Max(aFont->FontLineGap() - aFont->FontMaxHeight(),
+                   KSwtMinLinePadding);
+    iLineHeight = aFont->FontMaxHeight();
+
+    TRAP_IGNORE(BuildDrawableFragmentsListL(TextRect()));
+}
+
+// ---------------------------------------------------------------------------
+// From class ASwtControlBase.
+// ---------------------------------------------------------------------------
+//
+const MSwtFont& CSwtLink::DefaultFont() const
+{
+    return *iDefaultFont;
+}
+
+
+// ---------------------------------------------------------------------------
+// From class ASwtControlBase.
+// ---------------------------------------------------------------------------
+//
+HBufC* CSwtLink::MSKLabelL() const
+{
+    return iEikonEnv->AllocReadResourceL(R_QTN_MSK_SELECT);
+}
+
+
+// ---------------------------------------------------------------------------
+// From class ASwtControlBase.
+// ---------------------------------------------------------------------------
+//
+TBool CSwtLink::IsKeyUsed(TUint aKeyCode) const
+{
+    TBool keyUsed(EFalse);
+    if (aKeyCode == EKeyOK ||
+            aKeyCode == EKeyEnter ||
+            aKeyCode == EKeyLeftArrow ||
+            aKeyCode == EKeyRightArrow ||
+            aKeyCode == EKeyUpArrow ||
+            aKeyCode == EKeyDownArrow
+       )
+    {
+        keyUsed = ETrue;
+    }
+    return keyUsed;
+}
+
+// ---------------------------------------------------------------------------
+// From class ASwtControlBase.
+// ---------------------------------------------------------------------------
+//
+MSwtControl* CSwtLink::Control()
+{
+    return this;
+}
+
+
+// ---------------------------------------------------------------------------
+// From class MSwtLink.
+// ---------------------------------------------------------------------------
+//
+void CSwtLink::SetLinkDataL(
+    const CDesCArray* aTextFragments,
+    const CDesCArray* aLinkTargets)
+{
+    ASSERT(aTextFragments);
+    ASSERT(aLinkTargets);
+    ASSERT(aTextFragments->Count() == aLinkTargets->Count());
+
+    const TInt textLength = BuildFragmentListL(aTextFragments, aLinkTargets);
+    AssembleLinkTextL(aTextFragments, textLength);
+
+    //Prepare formatted text container
+    iFormattedText.Close();
+    iFormattedText.CreateL(textLength);
+
+    BuildDrawableFragmentsListL(TextRect());
+
+    SetFocusedFragment();
+
+    Redraw();
+}
+
+/**
+ * If control has focus, this method finds first active
+ * link fragment and sets focus to it.
+ */
+void CSwtLink::SetFocusedFragment()
+{
+    if (IsFocused())
+    {
+        iFocusedFragment = FindFirstActiveFragment();
+    }
+    else
+    {
+        iFocusedFragment = NULL;
+    }
+}
+
+TBool CSwtLink::SetSwtFocus(TInt aReason /*= KSwtFocusByApi*/)
+{
+    TBool prevFocused = IsFocusControl();
+    TBool res = ASwtControlBase::SetSwtFocus(aReason);
+
+    // Gaines focus by pointer
+    if (IsFocusControl() && !prevFocused)
+    {
+        iFocusChanged = ETrue;
+    }
+
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+// CSwtLinkFragmentDescriptor
+// ---------------------------------------------------------------------------
+//
+/**
+ * Creates new instance of non-active link fragment descriptor.
+ * @param aOffset Fragment text offset from the beggining of the link
+ *                plain text.
+ * @param aLength Fragment text length
+ */
+CSwtLinkFragmentDescriptor* CSwtLinkFragmentDescriptor::NewL(
+    TInt aOffset,
+    TInt aLength)
+{
+    CSwtLinkFragmentDescriptor* self =
+        new(ELeave) CSwtLinkFragmentDescriptor(aOffset, aLength);
+    return self;
+}
+
+/**
+ * Creates new instance of active link fragment descriptor.
+ * @param aOffset Fragment text offset from the beggining of the link
+ *                plain text.
+ * @param aLength Fragment text length
+ * @param aTarget Text passed to selection listner.
+ */
+CSwtLinkFragmentDescriptor* CSwtLinkFragmentDescriptor::NewL(
+    TInt aOffset,
+    TInt aLength,
+    const TDesC& aTarget)
+{
+    CSwtLinkFragmentDescriptor* self =
+        new(ELeave) CSwtLinkFragmentDescriptor(aOffset, aLength);
+    CleanupStack::PushL(self);
+    self->ConstructL(aTarget);
+    CleanupStack::Pop(self);
+    return self;
+}
+
+/**
+ * Constructor.
+ * @param aOffset Fragment text offset from the beggining of the link
+ *                plain text.
+ * @param aLength Fragment text length
+ */
+CSwtLinkFragmentDescriptor::CSwtLinkFragmentDescriptor(
+    TInt aOffset,
+    TInt aLength)
+        : iOffset(aOffset)
+        , iLength(aLength)
+        , iTarget(NULL)
+{
+}
+
+/**
+ * Constructs current insance.
+ * @param aTarget String to be passed to selection listener.
+ */
+void CSwtLinkFragmentDescriptor::ConstructL(const TDesC& aTarget)
+{
+    iTarget = aTarget.AllocL();
+}
+
+/**
+ * Destructor.
+ */
+CSwtLinkFragmentDescriptor::~CSwtLinkFragmentDescriptor()
+{
+    if (iTarget)
+    {
+        delete iTarget;
+        iTarget = NULL;
+    }
+}
+
+/**
+ * Returns offset from the beggining of the link text where the fragment
+ * begins.
+ */
+TInt CSwtLinkFragmentDescriptor::Offset() const
+{
+    return iOffset;
+}
+
+/**
+ * Returns the fragment text length.
+ */
+TInt CSwtLinkFragmentDescriptor::Length() const
+{
+    return iLength;
+}
+
+/**
+ * Returns string passed to selection listenr for active link fragments
+ * or NULL for non-active fragments.
+ */
+const TDesC* CSwtLinkFragmentDescriptor::Target() const
+{
+    return iTarget;
+}
+
+
+// ---------------------------------------------------------------------------
+// CSwtDrawableLinkFragment
+// ---------------------------------------------------------------------------
+//
+
+/**
+ * Creates new instance
+ * @param aText Text asociated to drawable fragment.
+ * @param aRect Rectangle into which fragment should be drawn.
+ * @param aFragmentDescriptor Fragment descriptor to which this drawable
+ *                            fragment belongs.
+ */
+CSwtDrawableLinkFragment* CSwtDrawableLinkFragment::NewL(
+    const TPtrC& aText,
+    const TRect& aRect,
+    const CFont& aFont,
+    const CSwtLinkFragmentDescriptor* aFragmentDescriptor)
+{
+    CSwtDrawableLinkFragment* self =
+        new(ELeave) CSwtDrawableLinkFragment(aRect,
+                                             aFragmentDescriptor);
+    CleanupStack::PushL(self);
+    self->ConstructL(aText, aFont);
+    CleanupStack::Pop(self);
+    return self;
+}
+
+/**
+ * Destructor.
+ */
+CSwtDrawableLinkFragment::~CSwtDrawableLinkFragment()
+{
+    iFormattedText.Close();
+}
+
+/**
+ * Constructor
+ * @param aText Text asociated to drawable fragment.
+ * @param aRect Rectangle into which fragment should be drawn.
+ * @param aFragmentDescriptor Fragment descriptor to which this drawable
+ *                            fragment belongs.
+ */
+CSwtDrawableLinkFragment::CSwtDrawableLinkFragment(
+    const TRect& aRect,
+    const CSwtLinkFragmentDescriptor* aFragmentDescriptor)
+        : iRect(aRect)
+        , iFragmentDescriptor(aFragmentDescriptor)
+{
+}
+
+/**
+ * Constructs the instance.
+ */
+void CSwtDrawableLinkFragment::ConstructL(const TPtrC& aText,
+        const CFont& aFont)
+{
+    iFormattedText.CreateL(aText.Length() + KAknBidiExtraSpacePerLine);
+    const TInt width = iRect.Width();
+    AknBidiTextUtils::ConvertToVisualAndClip(aText, iFormattedText, aFont,
+            width, width);
+}
+
+/**
+ * Return fragment descriptor to which this drawable fragment belongs.
+ */
+const CSwtLinkFragmentDescriptor*
+CSwtDrawableLinkFragment::FragmentDescriptor() const
+{
+    return iFragmentDescriptor;
+}
+
+/**
+ * Returns rectangle into which drawable fragment should be drawn.
+ */
+const TRect& CSwtDrawableLinkFragment::Rect() const
+{
+    return iRect;
+}
+
+/**
+ * Returns text belonging to the drawable fragment.
+ */
+const TDes& CSwtDrawableLinkFragment::Text() const
+{
+    return iFormattedText;
+}
+