Resource File Definition

The dialog was defined in a resource labeled

R_EXAMPLE_WRITE_FILE_DIALOG.Hereitis,from streams.rss:

RESOURCE DIALOG r_example_write_file_dialog {

buttons = R_EIK_BUTTONS_CANCEL_OK;

flags = EEikDialogFlagWait;

DLG_LINE {

prompt="File name"; id=EExampleControlIdFileName; type=EEikCtFileNameEd; control=FILENAMEEDITOR

DLG_LINE {

type = EEikCtEdwin;

id = EExampleControlIdText;

control = EDWIN { width = 25; maxlength = 256; }; }

Simply put, the dialog has a title, standard OK and Cancel buttons, some flags, and some items. In keeping with guidelines for positioning buttons, the Cancel/OK buttons are indicated by the resource name r_eik_buttons_cancel_ok.

Almost all dialogs should be coded with the line flags = EEikDialogFlagWait, which makes the dialog modal. Note

Regrettably, this is not the default. The default behavior is that your application can continue executing while the dialog is displayed. This isn't quite the same as a nonmodal dialog. Nonwaiting dialogs are typically used for activities like progress monitoring, in which the application is busy while the dialog is being displayed. Because the application is busy, it doesn't accept user input, so a nonwaiting dialog is effectively modal.

The body of the dialog is a vertical list of controls, each of which has

■ an ID, such as EExampleControlldText;

■ a type, such as EEikCtEdwin, which may have some initialization data of a format corresponding to the type, such as EDWIN { width= 25; maxlength = 256; }.

Don't use too many controls. As far as C++ is concerned, there is no limit, but in UIQ, for example, more than eight won't fit nicely onto a screen only 320 pixels high. If you code too many controls, the dialog becomes scrollable, and the user will have to scroll it to see all the controls. The dialog begins to overwhelm users with too much choice, making it hard to use. In some UIs, having too many controls will cause the dialog to overflow the screen, making it effectively unusable.

The prompt serves to identify its purpose to the user, while the ID identifies the control to the programmer. Later, we'll see that control IDs are used by C++ programs to specify the controls whose values they want to set or read. Like command IDs, control IDs are defined in the application's .hrh file, so they can be accessed both by resource file definitions and C++ programs.

One of the controls used here, EEikCtEdwin, is an edit window; the edwin resource struct is required to initialize such a control. In this example, I specify the size of the control (25 characters) that affects the dialog layout, and the maximum length of the data (256 characters).

10.2.2 Dialog Code

The base class for all dialogs is CEikDialog. Any dialog you write in your application will derive from CEikDialog, and it will typically implement at least two member functions - one for initializing the dialog and one for processing the OK key (or the DONE key that is often used in UIQ).

'Read-only' dialogs, for displaying application data, need only implement the initialization function. Ultra-trivial dialogs, initialized entirely from resource files, needn't even implement the initialization function. More complex dialogs can implement many functions besides the two shown below: we'll return to this later on. All CEikDialog virtual functions have a donothing default implementation: you only override them if necessary.

The following code extract shows the declaration of CExampleWrite-FileDialog,from streams.cpp:

Note

That's right: streams.cpp, not streams. h. I've treated dialogs as being private to the app UI, so they were not given their own header.

The C++ constructor takes whatever parameter is necessary to connect the dialog to the outside world - in this case, my app Ui, since it's this dialog's job to set its iFileName and iText members.

class CExampleWriteFileDialog : public CEikDialog {

public:

CExampleWriteFileDialog(CExampleAppUi* aAppUi);

private:

// From CEikDialog void PreLayoutDynInitL(); // Initialization

TBool OkToExitL(Tint aKeycode); // Termination private:

CExampleAppUi* iAppUi;

Note

On reflection, this isn't actually a very good encapsulation of the interface: I should really have passed references to the iFileName and iText members to make it clear that the dialog is intended to alter them and nothing else.

Initialization is performed by PreLayoutDyninitL() .The 'prelayout' part of the name means that the data you put into the dialog here will influence its layout - dialogs are laid out automatically to incorporate the optimum size of controls for the initialization data supplied.

Here's PreLayoutDynlnitL () :

void CExampleWriteFileDialog::PreLayoutDynInitL() {

CEikFileNameEditor* fn amed=STATIC_CAST(CEikFileNameEditor*, Control

(EExampleControlIdFileName));

fnamed->SetTextL(iAppUi->iFileName); CEikEdwin* edwin=STATIC_CAST(CEikEdwin*, Control

(EExampleControlIdText));

edwin->SetTextL(iAppUi->iText); }

This simply sets the edit windows to the existing values in iFile-Name and iText. Controls are identified by their ID, as specified in the id= line in the resource file definition. Control () returns a CCoeControl* type and this must be cast to the actual control type that it represents.

OK is handled by OkToExitL(). In fact, pressing any of the dialog buttons - except Cancel - will result in a call to this function. The function extracts values from the controls and, if everything is OK, returns ETrue, causing the dialog to be dismissed. If there's a problem (if, say, the value for iFileName isn't actually a valid filename), then OkToExitL () may either leave or return EFalse, which will continue the dialog.

OkToExitL() is more complicated because it has to check the validity of the requested operation before returning control to the app UI:

TBool CExampleWriteFileDialog::OkToExitL(TInt /* aKeycode */) //

termination {

CEikEdwin* edwin = STATIC_CAST(CEikEdwin*,

Control(EExampleControlIdFileName));

// NB Cast as a CEikEdwin because only base // class functionality required. HBufC* fileName=edwin->GetTextInHBufL() ;

// Check it's even been specified if(!fileName) {

TryChangeFocusToL(EExampleControlIdFileName);

iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_NO_FILENAME_SPECIFIED); }

CleanupStack::PushL(fileName);

// Check it's a valid filename if(!iCoeEnv->FsSession().IsValidName(*fileName)) {

TryChangeFocusToL(EExampleControlIdFileName);

iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_INVALID_FILE_NAME); }

// Get the text string edwin = STATIC_CAST(CEikEdwin*, Control(EExampleControlIdText)); HBufC* text = edwin->GetTextInHBufL(); if(!text)

text = HBufC::NewL(0); CleanupStack::PushL(text);

// Ensure the directories etc. needed for the file exist

TInt err = iCoeEnv->FsSession().MkDirAll(*fileName);

if(err != KErrNone && err l= KErrAlreadyExists) User::Leave(err);

// Check whether it's going to be possible to create the file for writing RFile file;

err = file.Create(iCoeEnv->FsSession(), *fileName, EFileWrite); if(err != KErrNone && err != KErrAlreadyExists)

User::Leave(err); // No need to close file, since it didn't open

// Check whether the user wants to replace the file, if it already exists if(err == KErrAlreadyExists) {

if(iEikonEnv->QueryWinL(R_EIK_TBUF_FILE_REPLACE_CONFIRM)) User::LeaveIfError(file.Replace(iCoeEnv->FsSession(),

*fileName, EFileWrite));

else iEikonEnv->LeaveWithInfoMsg(0); // Let user try again

// Finished with user interaction: communicate parameters and return delete iAppUi->iFileName;

iAppUi->iFileName = fileName;

delete iAppUi->iText;

iAppUi->iText = text;

CleanupStack::Pop(2); // text, fileName return ETrue; }

On a dialog with only Cancel and OK buttons, there's no point in checking the key code: it can only be the code for OK. For that reason, I've commented out the aKeyCode parameter name. On a dialog with other buttons you would also have to check for those key codes.

The processing sequence is as follows:

■ Get the filename: check that it's nonempty, and check that it's a valid filename.

■ Get the text : if it's empty, turn it into a zero-length string (rather than no string at all), because my file format requires that a string be written, even if it's an empty one.

■ Make sure the directory exists.

■ Check whether the user wants to overwrite an existing file.

■ Store the values from the dialog.

Any of the checks or file operations in this sequence could fail, and the dialog is carefully coded to ensure that any such failure is entirely cleanup-safe. A User ::Leave() from OkToExitL() is trapped and ensures that the dialog doesn't exit. There are three types of leave from within this code:

■ iEikonEnv->LeaveWithInfoMsg (resource-id ): this prints an info- message and then leaves with a special error code that causes no error message to be displayed. This is the recommended option for leaving when you detect an error in user input. Use the info-message to identify the error. Rely on cleanup to delete temporary variables such as fileName and text that have been pushed to the cleanup stack.

■ User: :Leave() with a genuine error code, such as the error code from opening a file: the UI will display the error message appropriate for that code.

■ iEikonEnv->LeaveWithInfoMsg(0): this leaves without giving any message at all, but cleans up temporary variables. I use this variant when the user opts not to overwrite an existing file: it's obvious what the 'error' is at this point, so the user doesn't need to be told again.

The code above also shows how to use a query window. In this case, I want to check whether the user wants to overwrite an existing file. iEikonEnv->QueryWinL () takes a resource ID indicating the question to ask, and returns ETrue for a Yes answer, EFalse for No.

Note

In the PreLayoutDynInitL () code, the control for capturing the file name was cast as a CEikFileNameEditor.In OkToExitL () above, it's cast as a CEikEdwin. This is ok because CEikFile-NameEditor is derived from CEikEdwin and we only need to use the functionality provided by the base class.

0 0

Post a comment

  • Receive news updates via email from this site