diff -r 106971a9964d -r b0f0be18af85 tsrc/consoleplayer/common/testappbase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tsrc/consoleplayer/common/testappbase.cpp Fri Sep 17 08:31:33 2010 +0300 @@ -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 +#include +#include + +#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& 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(""), TPoint(KEntryColumn, row) ); + row += KRowIncrement; + + forwardIndex = offset; + offset++; + } + if( startIndex > 0 ) + { + if( selected == offset ) + { + iGc->SetUnderlineStyle(EUnderlineOn); + } + else + { + iGc->SetUnderlineStyle(EUnderlineOff); + } + + iGc->DrawText( _L(""), 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& aSelectionList, + TInt aInitialSelectionIndex ) + { + RPointerArray 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& 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 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 drives; + BuildDriveListL( drives ); + + RPointerArray 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& 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 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 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& 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; + } + }