mmserv/sts/tsrc/ststester/src/testappbase.cpp
changeset 14 80975da52420
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmserv/sts/tsrc/ststester/src/testappbase.cpp	Mon May 03 12:59:52 2010 +0300
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of "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:
+ * Source file containing common test app functionality.
+ */
+
+#include <f32file.h>
+#include <remconcoreapitarget.h>
+#include <remconinterfaceselector.h>
+
+#include "testappbase.h"
+
+const TInt KLeftSoftKeyScanCode = EStdKeyDevice0;
+const TInt KRightSoftKeyScanCode = EStdKeyDevice1;
+
+const TInt KHelpWindowBorderPixels = 20;
+const TInt KHelpWindowSpaceBetweenColumns = 25;
+const TInt KHelpWindowSpaceBetweenRows = 2;
+
+// TODO: WOULD BE BETTER TO DYNAMICALLY DETECT THE AVAILABLE DRIVES
+_LIT( KDriveC, "C:" );
+_LIT( KDriveE, "E:" );
+_LIT( KDriveF, "F:" );
+_LIT( KDriveZ, "Z:" );
+
+struct TKeyListEntry
+    {
+    TInt scanCode;
+    TInt scanCode2;
+    const TText* keyName;
+    };
+
+// Some emulators return '1' when key 1 is pressed and others returned EStdKeyNkp1.
+// Convert both case into '1'.
+const TKeyListEntry KSupportedKeys[KSupportedKeysCount] =
+    {
+        {EStdKeyEnter,      EStdKeyNull, STR("Enter")},
+        {EStdKeyUpArrow,    EStdKeyNull, STR("Up")},
+        {EStdKeyDownArrow,  EStdKeyNull, STR("Down")},
+        {EStdKeyLeftArrow,  EStdKeyNull, STR("Left")},
+        {EStdKeyRightArrow, EStdKeyNull, STR("Right")},
+        {'0',               EStdKeyNkp0, STR("0")},
+        {'1',               EStdKeyNkp1, STR("1")},
+        {'2',               EStdKeyNkp2, STR("2")},
+        {'3',               EStdKeyNkp3, STR("3")},
+        {'4',               EStdKeyNkp4, STR("4")},
+        {'5',               EStdKeyNkp5, STR("5")},
+        {'6',               EStdKeyNkp6, STR("6")},
+        {'7',               EStdKeyNkp7, STR("7")},
+        {'8',               EStdKeyNkp8, STR("8")},
+        {'9',               EStdKeyNkp9, STR("9")}
+    };
+
+static TPtrC KeyName(TInt aIndex)
+    {
+    //TODO: Check aIndex range
+    return TPtrC(KSupportedKeys[aIndex].keyName);
+    }
+
+// Portable app implementation
+
+CTestAppBase::CTestAppBase(TInt aFontSize) :
+    CActive(EPriorityStandard), iFontSize(aFontSize),
+            iHelpSemitransparentBackgroundActive(true)
+    {
+    CActiveScheduler::Add(this);
+    }
+
+CTestAppBase::~CTestAppBase()
+    {
+    delete iInterfaceSelector;
+    delete iHelpWindow;
+    delete iSelectionWindow;
+    delete iGc;
+    delete iWindowGroup;
+    delete iScreenDevice;
+    delete iTypefaceStore;
+    iWs.Close();
+    iFs.Close();
+    iFileHistory.ResetAndDestroy();
+    }
+
+void CTestAppBase::BaseConstructL(const TOperationsPage* aKeyMap,
+        TInt aPageCount)
+    {
+    iKeyMap = aKeyMap;
+    iPageCount = aPageCount;
+
+    User::LeaveIfError(iFs.Connect());
+
+    User::LeaveIfError(iWs.Connect());
+
+    iScreenDevice = new (ELeave) CWsScreenDevice(iWs);
+    User::LeaveIfError(iScreenDevice->Construct());
+    iDisplaySize = iScreenDevice->SizeInPixels();
+
+    User::LeaveIfError(iScreenDevice->CreateContext(iGc));
+
+    iWindowGroup = new (ELeave) RWindowGroup(iWs);
+    User::LeaveIfError(iWindowGroup->Construct(KNullWsHandle));
+
+    iSelectionWindow = new (ELeave) RWindow(iWs);
+    User::LeaveIfError(iSelectionWindow->Construct(*iWindowGroup,
+            KNullWsHandle));
+    iSelectionWindow->SetVisible(false);
+    iSelectionWindow->Activate();
+
+    // Load the font to be used for all text operations.
+    TFontSpec fontSpec;
+    fontSpec.iHeight = iFontSize;
+    iTypefaceStore = CFbsTypefaceStore::NewL(NULL);
+
+    User::LeaveIfError(iTypefaceStore->GetNearestFontToDesignHeightInPixels(
+            iFont, fontSpec));
+
+    CalculateHelpWindowSize();
+
+    iHelpWindowTopRight = TPoint(iDisplaySize.iWidth / 2
+            - iHelpWindowSize.iWidth / 2, iDisplaySize.iHeight / 2
+            - iHelpWindowSize.iHeight / 2);
+
+    iHelpWindow = new (ELeave) RWindow(iWs);
+    User::LeaveIfError(iHelpWindow->Construct(*iWindowGroup, KNullWsHandle));
+    iHelpWindow->SetExtent(iHelpWindowTopRight, iHelpWindowSize);
+    iHelpWindow->SetTransparencyAlphaChannel();
+    iHelpWindow->SetBackgroundColor(KRgbTransparent);
+    iHelpWindow->SetVisible(false);
+    iHelpWindow->Activate();
+
+    iSoftkeyIndex = iKeyMap[iCurrentPage].defaultSoftkeyIndex;
+
+    // Only need to draw the help text when the options page is changed.  Window is displayed later by
+    // toggling the visibility of the window.
+    DrawHelpText();
+
+    // THE FOLLOWING CODE IS COMMENTED OUT BECAUSE IT CAUSES A CRASH IN NCP BUILDS.
+    // THIS CAN BE ENABLED IN DFS BUILDS, TO ALLOW FOR TWO BUTTON OPERATION USING THE VOLUME KEYS.
+    //
+    // Since some phone have no keyboard or soft keys, treat the volume keys like the soft keys.
+    // SetupVolumeKeysL();    
+    }
+
+void CTestAppBase::SetupVolumeKeysL()
+    {
+    iInterfaceSelector = CRemConInterfaceSelector::NewL();
+    iCoreTarget = CRemConCoreApiTarget::NewL(*iInterfaceSelector, *this);
+    iInterfaceSelector->OpenTargetL();
+    }
+
+void CTestAppBase::StartMonitoringWindowEvents()
+    {
+    // Request notification for windows server events, to detect key presses.    
+    SetActive();
+    iWs.EventReady(&iStatus);
+    }
+
+TInt CTestAppBase::CurrentPageNumber()
+    {
+    return iCurrentPage + 1;
+    }
+
+TPtrC CTestAppBase::CurrentPageName()
+    {
+    return TPtrC(iKeyMap[iCurrentPage].pageName);
+    }
+
+TPtrC CTestAppBase::CurrentSoftkeyName()
+    {
+    return TPtrC(iKeyMap[iCurrentPage].mapping[iSoftkeyIndex].text);
+    }
+
+TPtrC CTestAppBase::KeyMapText(TInt aIndex, TInt aPage)
+    {
+    return TPtrC(iKeyMap[aPage].mapping[aIndex].text);
+    }
+
+TInt CTestAppBase::KeyMapOperation(TInt aIndex, TInt aPage)
+    {
+    return iKeyMap[aPage].mapping[aIndex].operation;
+    }
+
+void CTestAppBase::IncrementKeymapIndex(TInt& aIndex, TInt aPage)
+    {
+    aIndex = (aIndex + 1) % KSupportedKeysCount;
+    while (iKeyMap[aPage].mapping[aIndex].operation == KOperation_None)
+        {
+        aIndex = (aIndex + 1) % KSupportedKeysCount;
+        }
+    }
+
+void CTestAppBase::DecrementKeymapIndex(TInt& aIndex, TInt aPage)
+    {
+    aIndex = (aIndex + KSupportedKeysCount - 1) % KSupportedKeysCount;
+    while (iKeyMap[aPage].mapping[aIndex].operation == KOperation_None)
+        {
+        aIndex = (aIndex + KSupportedKeysCount - 1) % KSupportedKeysCount;
+        }
+    }
+
+void CTestAppBase::CalculateHelpWindowSize()
+    {
+    iHelpWindowColumn1Width = 0;
+    iHelpWindowColumn2Width = 0;
+
+    // Find the widest strings for each column to determine the width of the window.
+    for (TInt index = 0; index < KSupportedKeysCount; index++)
+        {
+        TInt width = iFont->TextWidthInPixels(KeyName(index));
+        if (width > iHelpWindowColumn1Width)
+            {
+            iHelpWindowColumn1Width = width;
+            }
+
+        for (TInt index2 = 0; index2 < iPageCount; index2++)
+            {
+            width = iFont->TextWidthInPixels(KeyMapText(index, index2));
+            if (width > iHelpWindowColumn2Width)
+                {
+                iHelpWindowColumn2Width = width;
+                }
+            }
+        }
+
+    iHelpWindowSize.iWidth = 2 * KHelpWindowBorderPixels
+            + iHelpWindowColumn1Width + KHelpWindowSpaceBetweenColumns
+            + iHelpWindowColumn2Width;
+
+    iHelpWindowSize.iHeight = 2 * KHelpWindowBorderPixels + iFontSize
+            * KSupportedKeysCount + KHelpWindowSpaceBetweenRows
+            * (KSupportedKeysCount - 1);
+    }
+
+CTestAppBase::TTestAppPointerEvent CTestAppBase::CharacterizePointerEvent(
+        TAdvancedPointerEvent& event)
+    {
+    TTestAppPointerEvent returnValue = EPointerEvent_None;
+
+    RDebug::Printf("POINTER EVENT:");
+    RDebug::Printf("iType=%i", event.iType);
+    RDebug::Printf("iModifiers=%x", event.iModifiers);
+    RDebug::Printf("iPosition=%i,%i", event.iPosition.iX, event.iPosition.iY);
+    RDebug::Printf("iParentPosition=%i,%i", event.iParentPosition.iX,
+            event.iParentPosition.iY);
+    RDebug::Printf("PointerNumber=%i", event.PointerNumber());
+    RDebug::Printf("Proximity=%i", event.Proximity());
+    RDebug::Printf("Pressure=%i", event.Pressure());
+    RDebug::Printf("ProximityAndPressure=%i", event.ProximityAndPressure());
+    RDebug::Printf("Position3D=%i,%i,%i", event.Position3D().iX,
+            event.Position3D().iY, event.Position3D().iZ);
+    RDebug::Printf("Pressure3D=%i,%i,%i", event.Pressure3D().iX,
+            event.Pressure3D().iY, event.Pressure3D().iZ);
+    RDebug::Printf("PositionAndPressure3D=%i,%i,%i",
+            event.PositionAndPressure3D().iX,
+            event.PositionAndPressure3D().iY,
+            event.PositionAndPressure3D().iZ);
+
+    switch (event.iType)
+        {
+        case TPointerEvent::EButton1Down:
+            {
+            iPointerDownPosition = event.iPosition;
+            break;
+            }
+        case TPointerEvent::EButton1Up:
+            {
+            TInt xDelta = event.iPosition.iX - iPointerDownPosition.iX;
+            TInt yDelta = event.iPosition.iY - iPointerDownPosition.iY;
+
+            TInt xMagnitude = xDelta;
+            if (xMagnitude < 0)
+                {
+                xMagnitude = -xMagnitude;
+                }
+
+            TInt yMagnitude = yDelta;
+            if (yMagnitude < 0)
+                {
+                yMagnitude = -yMagnitude;
+                }
+
+            const TInt KTapThreshold = 30;
+
+            if (yMagnitude > xMagnitude)
+                {
+                if (yMagnitude < KTapThreshold)
+                    {
+                    RDebug::Printf("POINTER EVENT ENTER x=%i y=%i", xDelta,
+                            yDelta);
+                    returnValue = EPointerEvent_Select;
+                    }
+                else if (yDelta < 0)
+                    {
+                    RDebug::Printf("POINTER EVENT UP x=%i y=%i", xDelta,
+                            yDelta);
+                    returnValue = EPointerEvent_Up;
+                    }
+                else
+                    {
+                    RDebug::Printf("POINTER EVENT DOWN x=%i y=%i", xDelta,
+                            yDelta);
+                    returnValue = EPointerEvent_Down;
+                    }
+                }
+            else
+                {
+                if (xMagnitude < KTapThreshold)
+                    {
+                    RDebug::Printf("POINTER EVENT ENTER x=%i y=%i", xDelta,
+                            yDelta);
+                    returnValue = EPointerEvent_Select;
+                    }
+                else if (xDelta < 0)
+                    {
+                    RDebug::Printf("POINTER EVENT LEFT x=%i y=%i", xDelta,
+                            yDelta);
+                    returnValue = EPointerEvent_Left;
+                    }
+                else
+                    {
+                    RDebug::Printf("POINTER EVENT RIGHT x=%i y=%i", xDelta,
+                            yDelta);
+                    returnValue = EPointerEvent_Right;
+                    }
+                }
+            break;
+            }
+        }
+
+    return returnValue;
+    }
+
+void CTestAppBase::RunL()
+    {
+    if (iWait.IsStarted())
+        {
+        // This is an event during synchronous list selection.  Stop the nested scheduler.
+        iWait.AsyncStop();
+        return;
+        }
+
+    TWsEvent event;
+    iWs.GetEvent(event);
+
+    TInt operationIndex = -1;
+
+    TInt scanCode = 0;
+    bool processScanCode = false;
+
+    TInt operation = KOperation_None;
+
+    // Other potentially useful events are EEventKeyUp and EEventKeyDown.
+
+    if (event.Type() == EEventKey)
+        {
+        scanCode = event.Key()->iScanCode;
+
+        RDebug::Printf("key event %x %c", scanCode, scanCode);
+
+        // Allow subclasses a chance to consume the key event directly.  If that happens, then
+        // do not handle the key as normal.
+        if (!ConsumeKeyEvent(scanCode))
+            {
+            processScanCode = true;
+            }
+        }
+    else if (event.Type() == EEventPointer)
+        {
+        TAdvancedPointerEvent* p = event.Pointer();
+
+        TTestAppPointerEvent pointerEvent = CharacterizePointerEvent(*p);
+
+        switch (pointerEvent)
+            {
+            case EPointerEvent_None:
+                // Do nothing.
+                break;
+            case EPointerEvent_Up:
+                operation = KOperation_PreviousOption;
+                break;
+            case EPointerEvent_Down:
+                operation = KOperation_NextOption;
+                break;
+            case EPointerEvent_Left:
+                operation = KOperation_PreviousOptionPage;
+                break;
+            case EPointerEvent_Right:
+                operation = KOperation_NextOptionPage;
+                break;
+            case EPointerEvent_Select:
+                operation = KOperation_ExecuteOption;
+                break;
+            }
+        }
+
+    if (processScanCode)
+        {
+        // If one of the softkeys were pressed then take the appropriate action.
+        // This is to support a two button touch device with no numeric keypad.
+        // Support 'A' and 'B' also, for the NCP emulator where a keyboard  is
+        // not displayed.
+        switch (scanCode)
+            {
+            case KLeftSoftKeyScanCode:
+            case 'a':
+            case 'A':
+                {
+                operation = KOperation_NextOption;
+                break;
+                }
+            case KRightSoftKeyScanCode:
+            case 'b':
+            case 'B':
+                {
+                // Execute softkey function.
+                operation = KOperation_ExecuteOption;
+                break;
+                }
+            default:
+                {
+                // Search for scancode in keymap.  If not found then the key was not a valid
+                // key, so ignore.                
+                TInt index = 0;
+                while ((index < KSupportedKeysCount)
+                        && (operationIndex == -1))
+                    {
+                    if (KSupportedKeys[index].scanCode == scanCode
+                            || KSupportedKeys[index].scanCode2 == scanCode)
+                        {
+                        // Found!
+                        operationIndex = index;
+                        }
+                    else
+                        {
+                        index++;
+                        }
+                    }
+                break;
+                }
+            }
+        }
+
+    if (operation == KOperation_ExecuteOption)
+        {
+        operationIndex = iSoftkeyIndex;
+        }
+
+    if (operationIndex >= 0)
+        {
+        operation = KeyMapOperation(operationIndex, iCurrentPage);
+        }
+
+    if (operation != KOperation_None)
+        {
+        // Valid operation.
+
+        switch (operation)
+            {
+            case KOperation_Exit:
+                {
+                CActiveScheduler::Stop();
+                break;
+                }
+            case KOperation_PreviousOption:
+                {
+                // Change softkey function.
+                DecrementKeymapIndex(iSoftkeyIndex, iCurrentPage);
+
+                // Redraw help text, since a new function should now be underlined.
+                DrawHelpText();
+
+                // Notify subclass that softkey function has been updated.
+                SoftkeyFunctionUpdated();
+                break;
+                }
+            case KOperation_NextOption:
+                {
+                // Change softkey function.
+                IncrementKeymapIndex(iSoftkeyIndex, iCurrentPage);
+
+                // Redraw help text, since a new function should now be underlined.
+                DrawHelpText();
+
+                // Notify subclass that softkey function has been updated.
+                SoftkeyFunctionUpdated();
+                break;
+                }
+            case KOperation_PreviousOptionPage:
+                {
+                iCurrentPage = (iCurrentPage + iPageCount - 1) % iPageCount;
+                iSoftkeyIndex = iKeyMap[iCurrentPage].defaultSoftkeyIndex;
+                DrawHelpText();
+                SoftkeyFunctionUpdated();
+                break;
+                }
+            case KOperation_NextOptionPage:
+                {
+                iCurrentPage = (iCurrentPage + 1) % iPageCount;
+                iSoftkeyIndex = iKeyMap[iCurrentPage].defaultSoftkeyIndex;
+                DrawHelpText();
+                SoftkeyFunctionUpdated();
+                break;
+                }
+            case KOperation_ToggleHelpVisibility:
+                {
+                // Toggle help text on/off.
+                iHelpActive = !iHelpActive;
+                iHelpWindow->SetVisible(iHelpActive);
+                break;
+                }
+            case KOperation_ToggleHelpTransparency:
+                {
+                iHelpSemitransparentBackgroundActive
+                        = !iHelpSemitransparentBackgroundActive;
+                if (iHelpSemitransparentBackgroundActive)
+                    {
+                    // Turn on help if it is currently off.
+                    iHelpActive = true;
+                    iHelpWindow->SetVisible(true);
+                    }
+                DrawHelpText();
+                break;
+                }
+            default:
+                {
+                // Pass operation to subclass.
+                TPtrC operatioName(KeyMapText(operationIndex, iCurrentPage));
+                ExecuteOperation(operation, operatioName);
+                break;
+                }
+            }
+        }
+
+    SetActive();
+    iWs.EventReady(&iStatus);
+    }
+
+void CTestAppBase::DoCancel()
+    {
+    iWs.EventReadyCancel();
+    }
+
+// TODO: ALLOW SUBCLASS TO SPECIFY COLOR SELECTIONS
+
+TInt CTestAppBase::SelectFromListL(TPoint aTopLeft, TSize aSize,
+        const TDesC& aHeaderText, RPointerArray<TDesC>& aSelectionList,
+        TInt aInitialSelectionIndex)
+    {
+    iSelectionWindow->SetExtent(aTopLeft, aSize);
+    iSelectionWindow->SetVisible(true);
+
+    const TInt KRowIncrement = iFontSize + 2;
+
+    TInt entriesPerPage = aSize.iHeight / KRowIncrement - 4;
+
+    TInt returnValue = -2;
+    TInt startIndex = 0;
+    TInt selected = aInitialSelectionIndex;
+
+    while (returnValue == -2)
+        {
+        iGc->Activate(*iSelectionWindow);
+
+        iSelectionWindow->Invalidate();
+        iSelectionWindow->BeginRedraw();
+
+        iGc->Reset();
+
+        iGc->UseFont(iFont);
+        iGc->SetBrushColor(KRgbDarkBlue);
+
+        iGc->Clear();
+
+        // KRgbWhite seems to be having problems (0xffffff) in some emulators,
+        // but 0xfefefe is working, so use that instead of white.        
+        iGc->SetPenColor(0xfefefe);
+
+        const TInt KHeaderColumn = 5;
+        const TInt KEntryColumn = 15;
+
+        TInt row = KRowIncrement;
+
+        iGc->SetUnderlineStyle(EUnderlineOff);
+
+        iGc->DrawText(aHeaderText, TPoint(KHeaderColumn, row));
+        row += (KRowIncrement + 5);
+
+        TBool again = true;
+        TInt backIndex = -1;
+        TInt forwardIndex = -1;
+        TInt offset = 0;
+
+        while (again)
+            {
+            if (selected == offset)
+                {
+                iGc->SetUnderlineStyle(EUnderlineOn);
+                }
+            else
+                {
+                iGc->SetUnderlineStyle(EUnderlineOff);
+                }
+
+            if ((offset < entriesPerPage) && (startIndex + offset
+                    < aSelectionList.Count()))
+                {
+                iGc->DrawText(*aSelectionList[startIndex + offset], TPoint(
+                        KEntryColumn, row));
+                row += KRowIncrement;
+
+                offset++;
+                }
+            else
+                {
+                again = false;
+                if (startIndex + offset < aSelectionList.Count())
+                    {
+                    iGc->DrawText(_L("<page down>"),
+                            TPoint(KEntryColumn, row));
+                    row += KRowIncrement;
+
+                    forwardIndex = offset;
+                    offset++;
+                    }
+                if (startIndex > 0)
+                    {
+                    iGc->DrawText(_L("<page up>"), TPoint(KEntryColumn, row));
+                    row += KRowIncrement;
+
+                    backIndex = offset;
+                    offset++;
+                    }
+                }
+            }
+
+        iSelectionWindow->EndRedraw();
+
+        iGc->Deactivate();
+
+        TInt scanCode = WaitForAnyKey();
+
+        switch (scanCode)
+            {
+            case EStdKeyUpArrow:
+                if (selected == 0)
+                    {
+                    selected = offset - 1;
+                    }
+                else
+                    {
+                    selected -= 1;
+                    }
+                break;
+
+            case EStdKeyDownArrow:
+            case KLeftSoftKeyScanCode:
+            case 'a':
+            case 'A':
+                selected += 1;
+                if (selected == offset)
+                    {
+                    selected = 0;
+                    }
+                break;
+
+            case EStdKeyLeftArrow:
+                if (backIndex >= 0)
+                    {
+                    startIndex -= entriesPerPage;
+                    selected = 0;
+                    }
+                else
+                    {
+                    returnValue = -1;
+                    again = false;
+                    }
+                break;
+
+            case EStdKeyRightArrow:
+                if (forwardIndex >= 0)
+                    {
+                    startIndex += entriesPerPage;
+                    selected = 0;
+                    }
+                break;
+
+            case EStdKeyEnter:
+            case KRightSoftKeyScanCode:
+            case 'b':
+            case 'B':
+                if (selected == forwardIndex)
+                    {
+                    startIndex += entriesPerPage;
+                    selected = 0;
+                    }
+                else if (selected == backIndex)
+                    {
+                    startIndex -= entriesPerPage;
+                    selected = 0;
+                    }
+                else
+                    {
+                    returnValue = startIndex + selected;
+                    }
+                break;
+            }
+        }
+
+    iSelectionWindow->SetVisible(false);
+
+    return returnValue;
+    }
+
+bool CTestAppBase::SelectDriveL(TPoint aTopLeft, TSize aWindowSize,
+        const TDesC& aHeaderText, TDes& aDrive)
+    {
+    RPointerArray<TDesC> drives;
+
+    // Select drive letter.
+    drives.Append(&KDriveC);
+    drives.Append(&KDriveE);
+    drives.Append(&KDriveF);
+    drives.Append(&KDriveZ);
+
+    TInt index = SelectFromListL(aTopLeft, aWindowSize, aHeaderText, drives);
+
+    bool returnValue = false;
+
+    if (index >= 0)
+        {
+        returnValue = true;
+        aDrive.Copy(*(drives[index]));
+        aDrive.Append(_L("\\"));
+        }
+
+    drives.Reset();
+
+    return returnValue;
+    }
+
+bool CTestAppBase::SelectFileL(TPoint aTopLeft, TSize aWindowSize,
+        const TDesC& aHeaderText, const TDesC& aDrive, TDes& aFullFilename)
+    {
+    TFileName directory;
+
+    DoSelectFileL(aTopLeft, aWindowSize, aHeaderText, aDrive, 0, directory,
+            aFullFilename);
+
+    aFullFilename.Insert(0, directory);
+
+    return aFullFilename.Length() > 0;
+    }
+
+bool CTestAppBase::SelectFileWithHistoryL(TPoint aTopLeft, TSize aSize,
+        TDes& aFullFilename, const TDesC& aHistoryFilename,
+        TInt aMaxHistoryEntries)
+    {
+    RPointerArray<TDesC> selections;
+
+    selections.Append(&KDriveC);
+    selections.Append(&KDriveE);
+    selections.Append(&KDriveF);
+    selections.Append(&KDriveZ);
+
+    // Add file history to the end of the drive list.  Newest files are last, so add in reverse order.
+    ReadFileHistory(aHistoryFilename);
+    for (TInt index = iFileHistory.Count() - 1; index >= 0; index--)
+        {
+        selections.Append(iFileHistory[index]);
+        }
+
+    bool done = false;
+    bool selected = true;
+
+    while (!done)
+        {
+        TInt index = SelectFromListL(aTopLeft, aSize,
+                _L("Select drive or recent file:"), selections);
+
+        if (index < 0)
+            {
+            selected = false;
+            done = true;
+            }
+        else if (index < 4)
+            {
+            TBuf<10> drive;
+            drive.Copy(*(selections[index]));
+            drive.Append(_L("\\"));
+
+            done = SelectFileL(aTopLeft, aSize, _L("Select file:"), drive,
+                    aFullFilename);
+            }
+        else
+            {
+            // Remove the selected file from the history, so that it will pop up to the top of the list
+            // as the most recently selected file.
+            TInt historyIndex = iFileHistory.Count() - index + 3;
+            iFileHistory.Remove(historyIndex);
+            aFullFilename.Copy(*(selections[index]));
+
+            done = true;
+            }
+        }
+
+    if (selected)
+        {
+        AddToFileHistory(aFullFilename, aHistoryFilename, aMaxHistoryEntries);
+        }
+
+    selections.Reset();
+
+    return selected;
+    }
+
+bool CTestAppBase::SelectIntegerL(TPoint aTopLeft, TSize aSize,
+        const TDesC& aHeaderText, TInt aMin, TInt aMax, TInt& aSelection)
+    {
+    // currently no way to exit out of this selection
+
+    iSelectionWindow->SetExtent(aTopLeft, aSize);
+    iSelectionWindow->SetVisible(true);
+
+    bool done = false;
+
+    while (!done)
+        {
+        iGc->Activate(*iSelectionWindow);
+
+        iSelectionWindow->Invalidate();
+        iSelectionWindow->BeginRedraw();
+
+        iGc->Reset();
+
+        iGc->UseFont(iFont);
+        iGc->SetBrushColor(KRgbDarkBlue);
+
+        iGc->Clear();
+
+        // KRgbWhite seems to be having problems (0xffffff) in some emulators,
+        // but 0xfefefe is working, so use that instead of white.        
+        iGc->SetPenColor(0xfefefe);
+
+        TBuf<120> buffer;
+        buffer.Copy(aHeaderText);
+        buffer.AppendFormat(_L(" %i"), aSelection);
+
+        iGc->DrawText(buffer, TPoint(5, iFontSize + 2));
+
+        iSelectionWindow->EndRedraw();
+
+        iGc->Deactivate();
+
+        TInt scanCode = WaitForAnyKey();
+
+        switch (scanCode)
+            {
+            case EStdKeyUpArrow:
+                aSelection -= 10;
+                break;
+
+            case EStdKeyDownArrow:
+            case KLeftSoftKeyScanCode:
+            case 'a':
+            case 'A':
+                aSelection += 10;
+                break;
+
+            case EStdKeyLeftArrow:
+                aSelection--;
+                break;
+
+            case EStdKeyRightArrow:
+                aSelection++;
+                break;
+
+            case EStdKeyEnter:
+            case KRightSoftKeyScanCode:
+            case 'b':
+            case 'B':
+                done = true;
+                break;
+            }
+
+        if (aSelection > aMax)
+            {
+            aSelection = aMin;
+            }
+        else if (aSelection < aMin)
+            {
+            aSelection = aMax;
+            }
+        }
+
+    iSelectionWindow->SetVisible(false);
+
+    return true;
+    }
+
+TInt CTestAppBase::WaitForAnyKey()
+    {
+    TInt returnValue = 0;
+
+    bool done = false;
+
+    while (!done)
+        {
+        // Have to use this tricky nested active scheduler technique to allow the active object
+        // used to remap volume keys to run.
+        SetActive();
+        iWs.EventReady(&iStatus);
+        iWait.Start();
+
+        TWsEvent event;
+        iWs.GetEvent(event);
+
+        // Other potentially useful events are EEventKeyUp and EEventKeyDown.
+
+        if (event.Type() == EEventKey)
+            {
+            done = true;
+            returnValue = event.Key()->iScanCode;
+            }
+        else if (event.Type() == EEventPointer)
+            {
+            TAdvancedPointerEvent* p = event.Pointer();
+
+            TTestAppPointerEvent pointerEvent = CharacterizePointerEvent(*p);
+
+            switch (pointerEvent)
+                {
+                case EPointerEvent_None:
+                    // Do nothing.
+                    break;
+                case EPointerEvent_Up:
+                    returnValue = EStdKeyUpArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Down:
+                    returnValue = EStdKeyDownArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Left:
+                    returnValue = EStdKeyLeftArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Right:
+                    returnValue = EStdKeyRightArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Select:
+                    returnValue = EStdKeyEnter;
+                    done = true;
+                    break;
+                }
+            }
+        }
+
+    return returnValue;
+    }
+
+void CTestAppBase::ReadFileHistory(const TDesC& aHistoryFilename)
+    {
+    iFileHistory.Reset();
+
+    RFile historyFile;
+    TInt err = historyFile.Open(iFs, aHistoryFilename, EFileShareReadersOnly
+            | EFileStream | EFileRead);
+
+    if (err == KErrNone)
+        {
+        TInt historyFileSize;
+        historyFile.Size(historyFileSize);
+
+        RBuf8 contents;
+        contents.Create(historyFileSize);
+
+        historyFile.Read(contents, historyFileSize);
+
+        historyFile.Close();
+
+        TPtrC8 remaining(contents);
+
+        while (remaining.Length() > 0)
+            {
+            TInt separatorIndex = remaining.Locate('\n');
+
+            if (separatorIndex < 0)
+                {
+                separatorIndex = remaining.Length();
+                }
+
+            HBufC* filename = HBufC::NewL(separatorIndex);
+            TPtrC8 filename8 = remaining.Left(separatorIndex);
+            filename->Des().Copy(filename8);
+
+            iFileHistory.Append(filename);
+
+            TInt remainingLength = remaining.Length() - separatorIndex - 1;
+
+            if (remainingLength > 0)
+                {
+                remaining.Set(remaining.Right(remaining.Length()
+                        - separatorIndex - 1));
+                }
+            else
+                {
+                break;
+                }
+            }
+
+        contents.Close();
+        }
+    }
+
+void CTestAppBase::AddToFileHistory(const TDesC& aFilename,
+        const TDesC& aHistoryFilename, TInt aMaxHistoryEntries)
+    {
+    HBufC* filename = HBufC::NewL(aFilename.Length());
+    filename->Des().Copy(aFilename);
+    iFileHistory.Append(filename);
+
+    while (iFileHistory.Count() > aMaxHistoryEntries)
+        {
+        delete iFileHistory[0];
+        iFileHistory.Remove(0);
+        }
+
+    RFile historyFile;
+    TInt err = historyFile.Create(iFs, aHistoryFilename, EFileStream
+            | EFileWrite);
+    if (err == KErrAlreadyExists)
+        {
+        err = historyFile.Open(iFs, aHistoryFilename, EFileStream
+                | EFileWrite);
+        historyFile.SetSize(0);
+        }
+
+    if (err == KErrNone)
+        {
+        for (TInt index = 0; index < iFileHistory.Count(); index++)
+            {
+            TBuf8<KMaxFileName> filename8;
+            filename8.Copy(iFileHistory[index]->Des());
+            historyFile.Write(filename8);
+            historyFile.Write(_L8("\n"));
+            }
+        }
+
+    historyFile.Close();
+    }
+
+void CTestAppBase::DoSelectFileL(TPoint aTopRight, TSize aWindowSize,
+        const TDesC& aHeaderText, const TFileName& aDirectory,
+        TInt aDirectoryLevel, TDes& aSelectedDirectory,
+        TDes& aSelectedFilename)
+    {
+    RPointerArray<TDesC> fileNames;
+
+    ReadDirectoryEntriesL(aDirectory, fileNames);
+
+    TInt initialSelectionIndex = 0;
+    _LIT( KUp, ".." );
+    if (aDirectoryLevel > 0)
+        {
+        TFileName* newEntry = new (ELeave) TFileName;
+        newEntry->Copy(KUp);
+        fileNames.Insert(newEntry, 0);
+        initialSelectionIndex++;
+        }
+
+    bool done = false;
+
+    while (!done && (aSelectedFilename.Length() == 0))
+        {
+        TInt index = SelectFromListL(aTopRight, aWindowSize, aHeaderText,
+                fileNames, initialSelectionIndex);
+
+        if (index == -1)
+            {
+            done = true;
+            }
+        else if (fileNames[index]->Compare(KUp) == 0)
+            {
+            // Go up one directory.  Return to caller without setting aFilename
+            break;
+            }
+        else if ((*fileNames[index])[fileNames[index]->Length() - 1] == '\\')
+            {
+            // Directory selected.
+            TFileName directory;
+            directory.Copy(aDirectory);
+            directory.Append(*fileNames[index]);
+            DoSelectFileL(aTopRight, aWindowSize, aHeaderText, directory,
+                    aDirectoryLevel + 1, aSelectedDirectory,
+                    aSelectedFilename);
+            }
+        else
+            {
+            // File selected.                            
+            aSelectedDirectory.Copy(aDirectory);
+            aSelectedFilename.Copy(*fileNames[index]);
+            done = true;
+            }
+
+        }
+
+    fileNames.ResetAndDestroy();
+    }
+
+void CTestAppBase::ReadDirectoryEntriesL(const TFileName& aDirectoryName,
+        RPointerArray<TDesC>& aFileNames)
+    {
+    aFileNames.ResetAndDestroy();
+
+    RDir dir;
+    User::LeaveIfError(dir.Open(iFs, aDirectoryName, KEntryAttNormal
+            | KEntryAttDir));
+
+    TEntryArray entries;
+    TInt err = KErrNone;
+    while (err == KErrNone)
+        {
+        err = dir.Read(entries);
+
+        for (TInt index = 0; index < entries.Count(); index++)
+            {
+            TFileName* newTail = new (ELeave) TFileName;
+            newTail->Copy(entries[index].iName);
+            if (entries[index].IsDir())
+                {
+                newTail->Append(_L("\\"));
+                }
+            aFileNames.Append(newTail);
+            }
+        }
+
+    dir.Close();
+    }
+
+void CTestAppBase::DrawHelpText()
+    {
+    iGc->Activate(*iHelpWindow);
+
+    iHelpWindow->Invalidate();
+    iHelpWindow->BeginRedraw();
+
+    iGc->Reset();
+
+    iGc->UseFont(iFont);
+    iGc->SetBrushColor(KRgbTransparent);
+
+    iGc->Clear();
+
+    if (iHelpSemitransparentBackgroundActive)
+        {
+        iGc->SetPenColor(KRgbTransparent);
+        iGc->SetBrushStyle(CWindowGc::ESolidBrush);
+        iGc->SetBrushColor(TRgb(0x7f7f7faf));
+        iGc->DrawRect(TRect(iHelpWindowSize));
+        }
+
+    // KRgbWhite seems to be having problems (0xffffff) in some emulators,
+    // but 0xfefefe is working, so use that instead of white.        
+    iGc->SetPenColor(0xfefefe);
+
+    const TInt KColumn1 = KHelpWindowBorderPixels;
+    const TInt KColumn2 = KColumn1 + iHelpWindowColumn1Width
+            + KHelpWindowSpaceBetweenColumns;
+    const TInt KRowIncrement = iFontSize + KHelpWindowSpaceBetweenRows;
+
+    TInt row = iFontSize + KHelpWindowBorderPixels;
+
+    for (TInt index = 0; index < KSupportedKeysCount; index++)
+        {
+        iGc->SetUnderlineStyle(EUnderlineOff);
+
+        TPtrC text = KeyMapText(index, iCurrentPage);
+
+        iGc->DrawText(KeyName(index), TPoint(KColumn1, row));
+
+        if (index == iSoftkeyIndex)
+            {
+            iGc->SetUnderlineStyle(EUnderlineOn);
+            }
+        else
+            {
+            iGc->SetUnderlineStyle(EUnderlineOff);
+            }
+
+        iGc->DrawText(text, TPoint(KColumn2, row));
+
+        row += KRowIncrement;
+        }
+
+    iHelpWindow->EndRedraw();
+
+    iGc->Deactivate();
+    }
+
+void CTestAppBase::MrccatoCommand(TRemConCoreApiOperationId aOperationId,
+        TRemConCoreApiButtonAction /*aButtonAct*/)
+    {
+    // Treat volume up like the right soft key, and volume down like the left soft key.
+    TKeyEvent keyEvent;
+    keyEvent.iCode = 0;
+    keyEvent.iScanCode = 0;
+    keyEvent.iModifiers = 0;
+    keyEvent.iRepeats = 0;
+
+    switch (aOperationId)
+        {
+        case ERemConCoreApiVolumeUp:
+            keyEvent.iScanCode = KRightSoftKeyScanCode;
+            iWs.SimulateKeyEvent(keyEvent);
+            iWs.Flush();
+            break;
+        case ERemConCoreApiVolumeDown:
+            keyEvent.iScanCode = KLeftSoftKeyScanCode;
+            iWs.SimulateKeyEvent(keyEvent);
+            iWs.Flush();
+            break;
+        default:
+            break;
+        }
+    }