Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 66
Текст из файла (страница 66)
We have not done so in order to make the meaning of theseresources clearer in the text of this book.S60 applications that need to modify the content of their commandmenus should supply an implementation of the DynInitMenuPaneL()function of the application UI class, which is called each time a menu isabout to be displayed. The aResourceId parameter identifies the menuand aMenuPane points to a menu pane that has already been constructedfrom the relevant resource. For this application, the implementation is:void COandXAppUi::DynInitMenuPaneL(TInt aResourceId,CEikMenuPane* aMenuPane){if (aResourceId == R_OANDX_MENU)354A SIMPLE GRAPHICAL APPLICATION{if (iController->IsNewGame()){CEikMenuPaneItem::SData item;iCoeEnv->ReadResource(item.iText,iController-IsCrossTurn() ? R_OANDX_O_MOVES_FIRST :R_OANDX_X_MOVES_FIRST);item.iCommandId = EOandXSwitchTurn;item.iFlags = 0;item.iCascadeId = 0;aMenuPane->AddMenuItemL(item);aMenuPane->DeleteMenuItem(EOandXNewGame);}}}The function first checks that the resource ID is for the relevant menu,but does not modify the menu pane (which therefore displays the Newgame menu item) if a game is in progress.
Otherwise, a new menu itemis constructed, in a CEikMenuPaneItem::SData data structure, usingthe relevant resource text, and added to the menu pane, and the Newgame item is deleted.When the user selects a command, the S60 UI responds by callingthe HandleCommandL() function of the application UI class. This isnormally implemented via a switch statement, with each case corresponding to one of the possible command IDs:void COandXAppUi::HandleCommandL(TInt aCommand){switch(aCommand){case EEikCmdExit:case EAknSoftkeyExit:{SaveL();Exit();}break;case EOandXNewGame:{iController->Reset();iAppView->ResetView();}break;case EOandXSwitchTurn:{iController->SwitchTurn();}break;default:break;}}DIFFERENCES FOR UIQ 3355In the Noughts and Crosses application, each case, with the exceptionof the Exit command, corresponds to one of the command IDs specifiedin oandx.hrh and is handled simply by making the appropriate changesto the controller’s state.Depending on how the Exit command is initiated, it may have either ofthe standard command IDs shown in the above code.
The application’sresponse must always be to call Exit() but, in addition, it calls SaveL()to save the current game state in the application’s persistent data.12.2 Differences for UIQ 3One of the major influences in the development of UIQ 3 was the needto make it easier for developers to support multiple form factors (both interms of screen dimensions and use of portrait or landscape alignments)and a variety of input methods (touch screen or keyboard; menu-drivenor softkey-driven interaction) from a single code base.In order to facilitate such flexibility, all UIQ 3 applications needto derive their views from the new CQikViewBase or CQikMultiPageViewBase classes. The UIQ version of the Noughts and Crossesapplication derives its main view from the CQikViewBase view class(which is a subclass of CCoeControl) and not directly from CCoeControl itself.
However, since it only has one view, it makes no specificuse of the view architecture that is described in Chapter 14.The Application UI ClassThere is a small difference in the way that the application UI classcreates and initializes the View. Remember that the S60 ConstructL()function of the application UI class is implemented as:void COandXAppUi::ConstructL(){BaseConstructL(EAknEnableSkin);iEngine = COandXEngine::NewL();iController = COandXController::NewL();iAppView = COandXAppView::NewL(ClientRect());AddToStackL(iAppView); // Enable keypresses to the viewiStacked = ETrue;ReportWhoseTurn();}The destructor needed to take note of iStacked in order to determinewhether or not it should remove the view from the control stack.
For UIQ,the corresponding code is:void COandXAppUi::ConstructL(){356A SIMPLE GRAPHICAL APPLICATIONCQikAppUi::ConstructL(); // initiate the standard valuesiEngine = COandXEngine::NewL();iController = COandXController::NewL();// Create the view and add it to the frameworkiAppView = COandXView::NewL(*this);AddViewL(*iAppView);ReportWhoseTurn();}The most significant difference is that the UIQ version registers theview by calling AddViewL(), which also takes care of adding it to thecontrol stack. After this call, the application UI class has full ownershipof the view, so you do not have to worry about either deregistering theview or removing it from the control stack when the application UI classis destroyed.The View ClassThe majority of the remaining source code differences that are neededin a UIQ version of the application stem from the differences betweenCQikViewBase and CCoeControl.
So, while both forms of the viewown their component controls, the mechanisms of ownership and accessare different. A view that derives from CCoeControl, as in the S60version, creates its component controls (normally in its ConstructL()function) and stores pointers to them in specific items of member data. Inthe S60 version, the relevant ConstructL() code is:...for (TInt i = 0; i < KNumberOfTiles; i++){User::LeaveIfError(iTiles.Append(CreateTileL()));}...iStatusWin = COandXStatusWin::NewL(Window());The controls are normally explicitly deleted in the destructor:COandXAppView::∼COandXAppView(){for (TInt i=0; i<KNumberOfTiles; i++){delete iTiles[i];}iTiles.Close();delete iStatusWin;}As described more fully in Chapter 17, access to each child control isvia the CountComponentControls() and ComponentControl()functions that each component-owning control must implement:DIFFERENCES FOR UIQ 3357TInt COandXAppView::CountComponentControls() const{return KNumberOfTiles + 1;}CCoeControl* COandXAppView::ComponentControl(TInt aIndex) const{if (aIndex==KNumberOfTiles){return iStatusWin;}else{return const_cast<COandXTile*>(iTiles[aIndex]);}}CQikViewBase provides its own mechanism for component controlownership, so views that derive from this class do not need to provideexplicit data members to store pointers to the components, nor do theyneed to provide code in the destructor to delete them.
In addition,such views do not need to (and should not) provide implementations ofCountComponentControls() and ComponentControl().Instead, UIQ views add component controls – normally from withinthe view’s implementation of ViewConstructL() – to a built-in arraythat is initialized by a call to InitComponentArrayL() and thenaccessed via a Components() function, for example:void COandXView::ViewConstructL(){ViewConstructFromResourceL(R_OANDX_UI_CONFIGURATIONS);InitComponentArrayL();for (TInt i = 0; i < KNumberOfTiles; i++){COandXTile * tile = new(ELeave) COandXTile;AddControlLC(tile, i);tile->ConstructL(this);tile->SetFocusing(ETrue);CleanupStack::Pop(tile);}// Status window added, but not created hereAddControlLC(iStatusWin, KNumberOfTiles);CleanupStack::Pop(iStatusWin);iStatWinNotAppended = EFalse;// Status window now owned by the viewUpdateCommandsL(Controller().IsNewGame(), Controller().IsCrossTurn());SetFocusByIdL(0);}In the UIQ version of the application, as you can see from the abovecode, each tile is created and then appended to the array with a callto AppendLC() which, as its name implies, leaves a pointer to the358A SIMPLE GRAPHICAL APPLICATIONtile on the cleanup stack.
The tile is removed from the cleanup stackonce any potentially leaving initialization is complete. The view takesownership of all component controls that are added in this way and theyare automatically deleted when the view is destroyed.If you think carefully about the code sequence for adding a componentinto a view, you might begin to wonder if it is truly safe. For example,what if the actions performed by AddControlLC() cause a leavebefore the control is added to the cleanup stack? Furthermore, if theview takes ownership of the control, should AddControlLC() leavethe control on the cleanup stack at all? Could a leave before the controlis popped from the cleanup stack result in the control being deletedtwice – once from the cleanup stack and once by the owning view? InSymbian OS, it is worth asking questions like this, since leave-safenessis paramount.In fact, this sequence is safe.
AddControlLC() adds the controlto the cleanup stack before performing any potentially leaving actions,and the item that is added to the cleanup stack is one that is specificallydesigned to transfer ownership of the view (in a leave-safe way) as itis popped.The creation of the status window follows a slightly different pattern.The reason for this is that the status window is accessed from thecontroller, via the ReportWhoseTurn() function of the application UIclass, and the first access occurs before the view’s ViewConstructL()has been called. (To optimize start-up time, particularly for applicationswith multiple views, ViewConstructL() is only called at the time thatthe view first needs to be made visible.) To prevent this attempted accessfrom causing a panic, we need to create the status window in the view’sConstructL() and add it in ViewConstructL().However, there is then a risk that the status window will not be deletedif the code leaves between its creation and it being successfully added.We solved this problem by including the iStatWinNotAppended flagin the class data, setting it when the status window is created and clearingit when the window has successfully been added to the array.
The view’sdestructor has to delete the status window only if the flag is set:COandXView::∼COandXView(){if (iStatWinNotAppended)// Once appended, system code takes care of a component’s deletion{delete iStatusWin;}}DIFFERENCES FOR UIQ 3359It is also worth pointing out that the initial construction of the view,from ViewConstructL(), is by means of a call to ViewConstructFromResourceL(). In suitable cases, an entire multi-page view canbe constructed from the resource accessed by this function, in the sameway that a dialog can be constructed from a resource, as described inChapter 16.