tsrc/consoleplayer/common/testappbase.cpp
changeset 33 e1b6d78dfe6a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tsrc/consoleplayer/common/testappbase.cpp	Fri Sep 03 19:37:02 2010 -0500
@@ -0,0 +1,1330 @@
+/*
+ * 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 KLeftSoftKeyCode  = EKeyDevice0;
+const TInt KRightSoftKeyCode = EKeyDevice1;
+
+const TInt KHelpWindowBorderPixels = 20;
+const TInt KHelpWindowSpaceBetweenColumns = 25;
+const TInt KHelpWindowSpaceBetweenRows = 2;
+
+struct TKeyListEntry
+    {
+    TInt         keyCode;
+    const TText* keyName;
+    };
+
+const TKeyListEntry KSupportedKeys[KSupportedKeysCount] =
+        {
+            {EKeyEnter,      STR("Enter")},      
+            {EKeyUpArrow,    STR("Up")},
+            {EKeyDownArrow,  STR("Down")}, 
+            {EKeyLeftArrow,  STR("Left")},
+            {EKeyRightArrow, STR("Right")},
+            {'0',            STR("0")},
+            {'1',            STR("1")},  
+            {'2',            STR("2")}, 
+            {'3',            STR("3")},
+            {'4',            STR("4")},
+            {'5',            STR("5")},
+            {'6',            STR("6")},
+            {'7',            STR("7")},
+            {'8',            STR("8")},
+            {'9',            STR("9")},
+            {'*',            STR("*")},
+            {'#',            STR("#")}
+        };
+        
+static TPtrC KeyName( TInt aIndex )
+    {
+    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();
+    iHelpWindow->PointerFilter(EPointerFilterDrag, 0);
+
+    // Initialize soft key indices
+    for( TInt i = 0; i < aPageCount; i++ )
+        {
+        iSoftkeyIndices[i] = iKeyMap[i].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[iSoftkeyIndices[iCurrentPage]].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);
+    }
+
+void CTestAppBase::StartReceivingPointerEvents()
+    {
+    iRoutePointerEventsToApp = true;
+    }
+
+void CTestAppBase::StopReceivingPointerEvents()
+    {
+    iRoutePointerEventsToApp = false;
+    }
+
+//virtual void HandlePointerEvent( const TPointerEvent& aEvent ) {}
+
+CTestAppBase::TTestAppPointerEvent CTestAppBase::CharacterizePointerEvent( const 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 keyCode = 0;
+    bool processKeyCode = false;
+    
+    TInt operation = KOperation_None; 
+    
+    // Other potentially useful events are EEventKeyUp and EEventKeyDown.
+    
+    if( event.Type() == EEventKey )
+        {
+        keyCode = event.Key()->iCode;
+                   
+        RDebug::Printf( "key event keyCode=%x/%c scanCode=%x/%c", keyCode, keyCode, event.Key()->iScanCode, event.Key()->iScanCode );
+        
+        // Allow subclasses a chance to consume the key event directly.  If that happens, then
+        // do not handle the key as normal.
+        if( !ConsumeKeyEvent( keyCode ) )
+            {
+            processKeyCode = true;        
+            }
+        }
+    else if( event.Type() == EEventPointer )
+        {
+        TAdvancedPointerEvent* p = event.Pointer();
+
+        if( iRoutePointerEventsToApp  )
+            {
+            HandlePointerEvent( *p );
+            }
+        else
+            {        
+            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;
+                }
+            }
+        }
+    else
+        {
+        RDebug::Printf( "other event, type=%i", event.Type() );    
+        }
+        
+    if( processKeyCode )
+        {
+        // 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( keyCode )
+            {
+            case KLeftSoftKeyCode:
+            case 'a':
+            case 'A':
+                {
+                operation = KOperation_NextOption;
+                break;
+                } 
+            case KRightSoftKeyCode:
+            case 'b':
+            case 'B':
+                {
+                // Execute softkey function.
+                operation = KOperation_ExecuteOption;
+                break;
+                }
+            default:
+                {
+                // Search for keycode 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].keyCode == keyCode )
+                        {                
+                        // Found!
+                        operationIndex = index;
+                        }
+                    else
+                        {
+                        index++;
+                        }
+                    }
+                break;
+                }                
+            }        
+        }
+    
+    if( operation == KOperation_ExecuteOption )
+        {
+        operationIndex = iSoftkeyIndices[iCurrentPage];
+        }
+    
+    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( iSoftkeyIndices[iCurrentPage], 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( iSoftkeyIndices[iCurrentPage], 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;
+                DrawHelpText();
+                SoftkeyFunctionUpdated();                
+                break;
+                }
+            case KOperation_NextOptionPage:
+                {
+                iCurrentPage = (iCurrentPage + 1) % iPageCount;
+                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 )
+                    {
+                    if( selected == offset )
+                        {
+                        iGc->SetUnderlineStyle(EUnderlineOn);
+                        }
+                    else
+                        {
+                        iGc->SetUnderlineStyle(EUnderlineOff);            
+                        }
+                
+                    iGc->DrawText( _L("<page up>"), TPoint(KEntryColumn, row) );
+                    row += KRowIncrement;
+
+                    backIndex = offset;
+                    offset++;
+                    }
+                }
+            }
+        
+        iSelectionWindow->EndRedraw();
+       
+        iGc->Deactivate();
+
+        TInt keyCode = WaitForAnyKey();        
+        
+        switch( keyCode )
+            {
+            case EKeyUpArrow:
+                if( selected == 0 )
+                    {
+                    selected = offset-1;
+                    }
+                else
+                    {
+                    selected -= 1;
+                    }
+                break;
+                
+            case EKeyDownArrow:
+            case KLeftSoftKeyCode:                        
+            case 'a':
+            case 'A':
+                selected += 1;
+                if( selected == offset )
+                    {
+                    selected = 0;
+                    }
+                break;
+
+            case EKeyLeftArrow:
+                if( backIndex >= 0 )
+                    {
+                    startIndex -= entriesPerPage;
+                    selected = 0;                        
+                    }
+                else
+                    {
+                    returnValue = -1;
+                    again = false;
+                    }
+                break;
+                
+            case EKeyRightArrow:
+                if( forwardIndex >= 0 )
+                    {
+                    startIndex += entriesPerPage;
+                    selected = 0;                    
+                    }
+                break;
+
+            case EKeyEnter:
+            case KRightSoftKeyCode:                        
+            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;        
+    }
+
+TInt CTestAppBase::SelectFromListL( TPoint aTopLeft,
+                                    TSize aSize,
+                                    const TDesC& aHeaderText, 
+                                    RPointerArray<HBufC>& aSelectionList, 
+                                    TInt aInitialSelectionIndex )
+    {
+    RPointerArray<TDesC> aSelectionList2;
+    
+    for( TInt index = 0; index < aSelectionList.Count(); index++ )
+        {
+        aSelectionList2.Append( aSelectionList[index]);
+        }
+    
+    TInt returnValue = SelectFromListL( aTopLeft, aSize, aHeaderText, aSelectionList2, aInitialSelectionIndex );
+    
+    aSelectionList2.Close();
+    
+    return returnValue;
+    }
+
+void CTestAppBase::BuildDriveListL( RPointerArray<HBufC>& aDrives )
+    {
+    TDriveList driveList;
+    TInt err = iFs.DriveList( driveList );
+    
+    for( TInt index = 0; index < driveList.Length(); index++ )
+        {
+        if( driveList[index] != 0 )
+            {
+            HBufC* drive = HBufC::NewL( 2 );
+            drive->Des().Format( _L("%c:"), 'A' + index);
+            aDrives.Append( drive );
+            }
+        }
+    }
+
+bool CTestAppBase::SelectDriveL( TPoint aTopLeft,
+                                 TSize aWindowSize,
+                                 const TDesC& aHeaderText, 
+                                 TDes& aDrive )
+    {
+    RPointerArray<HBufC> drives;
+    BuildDriveListL( drives );
+    
+    TInt index = SelectFromListL( aTopLeft, aWindowSize, aHeaderText, drives );
+    
+    bool returnValue = false;
+    
+    if( index >= 0 )
+        {
+        returnValue = true;
+        aDrive.Copy( *(drives[index]) );
+        aDrive.Append( _L("\\") );
+        }
+    
+    drives.ResetAndDestroy();
+    
+    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,
+                                           const TDesC& aPrompt )
+    {
+    RPointerArray<HBufC> drives;
+    BuildDriveListL( drives );
+        
+    RPointerArray<TDesC> selections;
+
+    for( TInt index = 0; index < drives.Count(); index++ )
+        {
+        selections.Append( drives[index]);
+        }
+    TInt drivesBaseIndex = 0;
+        
+    // Add file history to the end of the drive list.  Newest files are last, so add in reverse order.
+    // For convenience, add the last history entry at the top, before the drive list.
+    ReadFileHistory( aHistoryFilename );
+    if( iFileHistory.Count() > 0 )
+        {
+        selections.Insert( iFileHistory[iFileHistory.Count()-1], 0 );        
+        drivesBaseIndex++;
+        }
+    for(TInt index = iFileHistory.Count()-2; index >= 0; index-- )
+        {        
+        selections.Append( iFileHistory[index]);
+        }
+
+    bool done = false;
+    bool selected = true;
+    
+    while( !done )
+        {
+        TInt index = SelectFromListL( aTopLeft, aSize, aPrompt, selections );
+
+        if( index < 0 )
+            {
+            selected = false;
+            done = true;
+            }        
+        else if( (index >= drivesBaseIndex) && (index - drivesBaseIndex < drives.Count()) )
+            {
+            TBuf<10> drive;
+            drive.Copy( *(selections[index]) );
+            drive.Append( _L("\\") );
+            
+            done = SelectFileL( aTopLeft, aSize, aPrompt, 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;
+            if( index == 0 )
+                {
+                historyIndex = iFileHistory.Count() - 1;
+                }
+            else
+                {
+                historyIndex = iFileHistory.Count() - index + drives.Count() - 1;
+                }
+            aFullFilename.Copy( *(selections[index]) );
+            delete iFileHistory[historyIndex];
+            iFileHistory.Remove( historyIndex );            
+
+            done = true;
+            }
+        }
+    
+    if( selected )
+        {
+        AddToFileHistory( aFullFilename, aHistoryFilename, aMaxHistoryEntries );        
+        }
+    
+    selections.Reset();
+    drives.ResetAndDestroy();
+    
+    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 keyCode = WaitForAnyKey();
+        
+        switch( keyCode )
+            {
+            case EKeyUpArrow:
+                aSelection -= 10;
+                break;
+                
+            case EKeyDownArrow:
+            case KLeftSoftKeyCode:                        
+            case 'a':
+            case 'A':
+                aSelection += 10;
+                break;
+
+            case EKeyLeftArrow:
+                aSelection--;
+                break;
+                
+            case EKeyRightArrow:
+                aSelection++;
+                break;
+
+            case EKeyEnter:
+            case KRightSoftKeyCode:                        
+            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()->iCode;
+            }
+        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 = EKeyUpArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Down:
+                    returnValue = EKeyDownArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Left:
+                    returnValue = EKeyLeftArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Right:
+                    returnValue = EKeyRightArrow;
+                    done = true;
+                    break;
+                case EPointerEvent_Select:
+                    returnValue = EKeyEnter;
+                    done = true;
+                    break;
+                }
+            }        
+        }
+    
+    return returnValue;
+    }
+
+TInt CTestAppBase::ReadFile( const TDesC& aFilename, RPointerArray<HBufC>& aContents )
+    {
+    RFile inputFile;
+    TInt err = inputFile.Open(iFs, aFilename, EFileShareReadersOnly | EFileStream | EFileRead );
+    
+    if( err == KErrNone )
+        {
+        TInt fileSize;
+        inputFile.Size( fileSize );
+        
+        RBuf8 contents;
+        contents.Create( fileSize );
+
+        inputFile.Read( contents, fileSize );
+
+        inputFile.Close();
+
+        TPtrC8 remaining( contents );
+        
+        while( remaining.Length() > 0 )
+            {
+            // Removing leading CR/LFs, if any.
+            while( remaining.Length() > 0 && (remaining[0] == '\n' || remaining[0] == '\r') )
+                {
+                remaining.Set(remaining.Right(remaining.Length()-1));            
+                }
+
+            if( remaining.Length() == 0 )
+                {
+                break;
+                }
+
+            // Find next CR/LF, or end of string.
+            TInt separatorIndex = 1;
+            while( (separatorIndex < remaining.Length()) && 
+                   (remaining[separatorIndex] != '\n') &&
+                   (remaining[separatorIndex] != '\r'))
+                {
+                separatorIndex++;
+                }
+                        
+            HBufC* line = HBufC::NewL( separatorIndex );
+            TPtrC8 line8 = remaining.Left( separatorIndex );
+            line->Des().Copy( line8 );
+        
+            aContents.Append( line );
+
+            TInt remainingLength = remaining.Length() - separatorIndex;            
+
+            remaining.Set(remaining.Right(remainingLength));            
+            }        
+        
+        contents.Close();
+        }
+    
+    return err;
+    }
+
+void CTestAppBase::ReadFileHistory( const TDesC& aHistoryFilename )
+    {
+    iFileHistory.ResetAndDestroy();
+    
+    ReadFile( aHistoryFilename, iFileHistory );
+    }
+
+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 < 0 || index >= fileNames.Count() )
+            {
+            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;
+    TInt directoryCount = 0;
+    while( err == KErrNone )
+        {
+        err = dir.Read( entries );
+                
+        for( TInt index = 0; index < entries.Count(); index++ )
+            {
+            // It was observed that not all devices sorted the list by default, so do an
+            // insertion sort here.            
+            TFileName* newEntry = new(ELeave) TFileName;
+            newEntry->Copy( entries[index].iName );
+            
+            // Put the directories first in the list.
+            if( entries[index].IsDir())
+                {
+                newEntry->Append( _L("\\") );
+                
+                TInt insertionIndex = 0;
+                while( insertionIndex < directoryCount && newEntry->CompareF(*aFileNames[insertionIndex]) > 0 )
+                    {
+                    insertionIndex++;
+                    }
+                aFileNames.Insert( newEntry, insertionIndex );
+                
+                directoryCount++;
+                }
+            else
+                {
+                TInt insertionIndex = directoryCount;
+                while( insertionIndex < aFileNames.Count() && newEntry->CompareF(*aFileNames[insertionIndex]) > 0 )
+                    {
+                    insertionIndex++;
+                    }
+                aFileNames.Insert( newEntry, insertionIndex );
+                }
+            }        
+        }      
+    
+    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 == iSoftkeyIndices[iCurrentPage] )
+            {
+            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.iCode = KRightSoftKeyCode;
+            iWs.SimulateKeyEvent(keyEvent);
+            iWs.Flush();
+            break;
+        case ERemConCoreApiVolumeDown:
+            keyEvent.iCode = KLeftSoftKeyCode;
+            iWs.SimulateKeyEvent(keyEvent);
+            iWs.Flush();
+            break;
+        default:
+            break;
+        }
+    }