Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


The filesystem browser application

This section introduces the generic Symbian OS UI framework using a simple example application. It describes the fundamental classes that form the application framework and compares the differences between an implementation of the filesystem browser application for S60 (3rd Edition) and for UIQ (3.0).

This section also discusses common strategies used to maximise code re-use when writing a GUI application to run on both UI platforms.

Contents

[Top]


The filesystem browser application

Now this tutorial has described what you need to start work on an application for S60 or UIQ, it will walk through a simple filesystem browser application ("FileBrowse") and explain some of the aspects of application development on these platforms. The example is a simple illustration of the fundamental application framework classes, working with application resource files, how to use a list box control and how to handle user input.

When the file browser application starts, it displays the 'C' and 'D' drives of the phone or emulator in a list box, giving the user to option to browse through the directories of either drive.

The start up view for the S60 filesystem...


The start up view for the S60 filesystem browser application.

The user sees folders and files, which are distinguished by different icons, and may open directories to view their contents, but may not open files, receiving an information message if they try to do so.

On UIQ, the user navigates by tapping on a directory to open it, or uses the up and down keys and the confirmation key to open the directory selected. On S60, the user navigates using the joystick and may either open a selected directory by pressing the confirmation key of the joystick, or the 'Open' option from the left soft key menu.

When a new folder is opened, the application scans its content and the content in the list box is updated to reflect it.

The directory browse view for the UIQ fi...


The directory browse view for the UIQ filesystem browser application.

A view of files and directories in the U...


A view of files and directories in the UIQ filesystem browser application.

The user navigates back up the directory hierarchy on UIQ or S60 by selecting the top 'go back' directory. In addition, on S60, the user may use the right soft key, to go back to the parent directory.

The directory browse view for the S60 fi...


The directory browse view for the S60 filesystem browser application.

A view of files and directories in the S...


A view of files and directories in the S60 filesystem browser application.

[Top]


Introducing the application framework

The classes described in this section provide the basic functionality of an application and provide the interface needed for the platform framework to drive the application. A quick summary of each of the classes follows; they will be described in more detail as the implementation of the filesystem browser application is discussed.

The core Symbian OS UI framework is called Uikon. One of the most important libraries within it is eikcore.dll, which contains the UI framework classes such as CEikApplication, CEikAppUI and CEikonEnv.

UIQ and S60 extend the the framework by adding libraries to provide platform-specific controls. The UIQ-specific library is called Qikon and the S60-specific library is called Avkon. You can view these as UI layers on top of the core Symbian OS UI. Each contains different components, appropriate to the platform in question, although, because they both have Uikon as a base, their APIs are often very similar.

When creating a UI application for either platform, you will derive from base classes supplied by the platform-specific libraries (which themselves usually derive from classes in the Symbian OS framework), and call framework functions specific to the platform. To do this, you will need to include the appropriate header files and link against libraries accordingly.

For example, for UIQ, you will link against qikctrl.lib, while for S60 the equivalent is avkon.lib. Likewise for header files; the header files to include for each of the application framework classes are shown in the table below.

Every Symbian OS GUI application, regardless of the UI platform on which it is based, will use the application framework architecture, which means it will derive from a particular set of UI-specific classes: application, document, application UI and application view.

Application

An object of this class is the first to be created by the GUI framework when the application starts. When instantiated, the object is used to identify the application, by returning its UID. It also creates the application’s document class.

Application document

The document class handles any persistent non-GUI data for the application and instantiates the application UI class.

Application UI

The application UI handles UI events generated from the user’s actions (except low-level touch screen events and alphanumeric keyboard input). It also instantiates the application’s default view.

Application view

This is the root GUI control which activates the main application window and acts as a container for other controls used.

The following table shows the generic Symbian OS application framework classes, with the S60 and UIQ equivalents:

Class

Generic Uikon class

S60 (Avkon)

S60 header file

UIQ (Qikon)

UIQ header file

Application

CEikApplication (which derives from CApaApplication)

CAknApplication

aknapp.h

CQikApplication

qikapplication.h

Document

CEikDocument (which derives from CApaDocument)

CAknDocument

akndoc.h

CQikDocument

qikdocument.h

Application UI

CEikAppUi

CAknAppUi

aknappui.h

CQikAppUi

qikappui.h

View

CCoeControl

CCoeControl

coecntrl.h

CQikViewBase (derives from CCoeControl and MCoeView)

qikviewbase.h

Note that the 'CEik' prefix of the generic Symbian OS classes is replaced with 'CQik' for UIQ classes and 'CAkn' for S60 classes. This convention is used throughout the UI application framework, for classes, headers and libraries. Additionally, in general, if a class does not have any prefix, such as CCoeControl, it is part of the generic Symbian OS set.

Note: If you're wondering why the classes are prefixed with 'CEik' rather than 'CUik', the reason is historical. The original Symbian OS UI framework was called Eikon, in the days when Symbian OS itself was known as 'EPOC'. The framework was renamed Uikon in a later release of Symbian OS, but the class names themselves were not changed.

[Top]


Can I write one application that runs on both S60 and UIQ platforms?

No. As the previous section described, although each of the platforms are based on the generic Symbian OS Uikon framework, S60 and UIQ each extend it differently. When you write an application for UIQ, you must use the UIQ specific classes to achieve the correct look and feel for the application. The resulting code will be different from that of an S60 application, which would use a set of classes, specific to S60. To appreciate the extent of the differences, compare the source code for the S60 filesystem browser which accompanies this tutorial with that for the UIQ version.

Splitting into an application engine and application UI

You can reduce the amount of work required to write an application to run on both S60 and UIQ platforms by separating the code into two parts:

The idea is to facilitate the code's portability simply by minimising the amount of code that will need to be ported. Typically, the application engine will need little or no modification while the application UI will need re-designing and re-coding. Splitting the code into an engine and UI also means that development can proceed in parallel, with separate specialist developers working on the engine and UI sections.

Not only can the engine code often be re-used without many changes; it can also be tested automatically on each platform by using generic console test code without UI components as described later in The filesystem browser engine class and console test code.

The split can be very distinct, with the engine forming a separate component, typically a dll, or it can be more simple, such as putting the implementation of the engine code in a separate source file, which can be shared by every version of the application. The filesystem browser example uses the second approach, because it has a single, very simple engine class, CRFsEngine, to illustrate the technique of splitting an application into a UI and an engine. The engine class is described later in The filesystem browser engine class.

The following diagrams show the class hierarchies for the filesystem browser application for both S60 and UIQ implementations.

Class diagram for the filesystem browser...


Class diagram for the filesystem browser on UIQ.

Class diagram for the filesystem browser...


Class diagram for the filesystem browser on S60.

[Top]


The filesystem browser UI classes

This section will examine each of the application framework classes in more detail and relate them to the implementation of the filesystem browser application on both S60 and UIQ platforms.

The application class

Every Symbian OS application must implement the following functions which are called by the framework as it starts the application. The first one is a non-leaving function which creates a new instance of the application class. For the filesystem browser example application used in this tutorial, the code is as follows:

EXPORT_C CApaApplication* NewApplication()
    {
    return new CFileBrowseApplication;
    }

This factory function is expected by the framework to have exactly this prototype. It constructs and returns an instance of the application class, or NULL if it cannot be instantiated.

The second function is the application’s entry point function, called E32Main(), which looks like this:

GLDEF_C TInt E32Main()
    {
    return EikStart::RunApplication(NewApplication);
    }

E32Main() calls EikStart::RunApplication(), passing as an argument a pointer to the factory function which creates an instance of the application class.

The application class represents the properties that are the same for every instance of the application. This includes the information specified in the registration file, for instance the caption and the capabilities, and other information, for instance the UID.

At a minimum, it must also implement two functions:

The application class in both S60 and UIQ versions of the filesystem browser is called CFileBrowseApplication and its implementation is identical in each. For AppDllUid():

TUid CFileBrowseApplication::AppDllUid() const
  {
  return KUidFileBrowseID; // Defined in filebrowseglobals.h
  }

This function is called by the framework to get the application’s UID, just after calling NewApplication(). One of the reasons it needs the UID is to check whether there is a running instance of the application that it can switch to. The value returned must be the same as the value of the UID3 value specified in the mmp file. Once the UID has been verified, the framework calls the CreateDocumentL() method, which is implemented for the filesystem browser as follows:

CApaDocument* CFileBrowseApplication::CreateDocumentL()
  {
  return CFileBrowseDocument::NewL(*this);
  }

The document class

As described above, the document class is constructed and returned by the application’s CreateDocumentL() function, which is called internally by the application framework. It represents the data that relates to a particular instance of the application, and typically owns the application's engine.

In the filesystem browser example, the document class does not store any data but could be extended, for example, to store the current directory opened by the user. If the application is closed and then restarted later, this stored value can be used to open the directory previously viewed, rather than default to view the root directories of the C and D drives on startup.

To implement persistent application data, the document class should store and restore the necessary information by overriding the StoreL() and RestoreL() methods, both of which are empty by default. Where an engine provides functions to save and restore its data, the document would call these.

Even if the application does not have savable data, an application must still instantiate the document class, which should implement the CreateAppUiL() function, inherited from the document’s base class, CEikDocument. This function instantiates an object of the application UI class. For both S60 and UIQ implementations of the filesystem browser, the code is the same:

CEikAppUi* CFileBrowseDocument::CreateAppUiL()
    {
    return new(ELeave) CFileBrowseAppUi;
    }

This function is called by the framework. Note that CreateAppUiL() only carries out first phase construction. In other words, it does not call the app UI's ConstructL() — the framework is responsible for calling this. The framework will also take ownership of the app UI object, so the destructor of the document class does not need to destroy it.

The application UI class

The application UI class is constructed and returned by the document’s CreateAppUiL() function, described above. Its second phase construction method, ConstructL() is an important method. It is called by the framework and should include a call to the base class second phase constructor (either CQikAppUi::ConstructL() for UIQ or CAknAppUi::ConstructL() for S60 – each of which ultimately calls CEikAppUi::BaseConstructL()). Among other things, the base class constructor method will read the application’s resource file and create the visible GUI elements.

The ConstructL() method should also construct the application view. For S60, the implementation is as follows:

void CFileBrowseAppUi::ConstructL()
  {
  CAknAppUi::ConstructL();
  iBaseView = CFileBrowseBaseView::NewL(ClientRect());
  iBaseView->SetMopParent(this);
  AddToStackL(iBaseView);
  }

For UIQ it differs slightly:

void CFileBrowseAppUi::ConstructL()
  {
  BaseConstructL();
  
  CFileBrowseBaseView* baseView = CFileBrowseBaseView::NewLC(*this);
  AddViewL(*baseView);
  CleanupStack::Pop(baseView);
  }

In the UIQ implementation, AddViewL() registers the view with the system and adds it implicitly to the control stack, which enables the view to receive key and pointer input. The first view that is added becomes the default view for the application.

In the S60 implementation of ConstructL(), this is done by calling AddToStackL() explicitly (the view is removed from the control stack in the application UI class destructor). The control stack has a prioritised list of controls that should be offered key events to process, and ensures that the events are offered to those controls in priority order. In addition to controls from GUI applications, the stack also contains controls associated with any front-end processors or active dialogs and handles debug key combinations.

The S60 implementation also calls SetMopParent() to set itself as the parent of the view control object.

The ownership of the view is transferred to the app UI, and it is later unregistered and deleted automatically in the app UI's destructor. It is interesting to note that, in S60, the derived application UI class takes ownership of the view object and is responsible for deleting it, while in the UIQ implementation, the base class takes this responsibility and the destructor for the application UI class does nothing at all. The destructor for the S60 application UI class is as follows:

CFileBrowseAppUi::~CFileBrowseAppUi()
    {
    if (iBaseView)
        {// Remove it from the control stack
        RemoveFromStack(iBaseView); 
        delete iBaseView;
        }
    }

In S60, the application UI class also handles command events, which may originate from a range of sources such as menu bars, or the soft keys. This is done by implementing the HandleCommandL() function:

void CFileBrowseAppUi::HandleCommandL(TInt aCommand)
  {
  ASSERT(iBaseView);
  switch (aCommand)
    {
    case ECmdFileBrowseExit: 
    // Generated by the left soft key 'Exit' option
    case EEikCmdExit: 
    // Generated by a system command to close the application
        Exit();
        break;
    case ECmdFileBrowseOpen:
        iBaseView->CmdOpenL();
        break;
    case EAknSoftkeyBack:
        iBaseView->CmdBackL();
        break;
    default: // All other commands are passed to the base class
        CAknAppUi::HandleCommandL(aCommand);
        break;
    }
  }

Here a command is an instruction, identified by an integer value known as a command ID. Command IDs are enumerated constants defined in header files that are traditionally given the extension .hrh. In the code shown above, the commands ECmdFileBrowseOpen and ECmdFileBrowseExit are defined by the filesystem browser example code (in filebrowse.hrh) while the other commands (EEikCmdExit, EAknSoftkeyBack and all other commands handled) are defined by the system.

In the S60 implementation, the UI framework calls HandleCommandL() when either of the menu options available from the left soft key are selected – which defers to the application view to handle the ‘Open’ command and calls Exit() to handle 'Exit'.

The menu invoked by a left soft key pres...


The menu invoked by a left soft key press in the S60 filesystem browser application.

The right soft key generates an EAknSoftkeyBack command (because it has been set up to do so in the resource file for the application, as described in the section on Filebrowse resource file for S60). The back command is also passed to the view class to handle. All other commands are passed to the CAknAppUi base class.

In UIQ, commands are usually handled by application views, which are discussed in the next section.

The application view

In a simple case, such as the filesystem browser which accompanies this tutorial, there is a single view class which derives from the Uikon base class, CCoeControl. In more complex examples, it is common for an application to use more than one view, in which case the application UI class is responsible for creating and destroying the views, registering them with the view server, adding them to the control stack, activating them, passing events to the correct view for handling and setting a default view.

For both the S60 and UIQ implementations of the filesystem browser, the CFileBrowseBaseView view class is responsible for the following:

The view class is where most of the divergence between code for the S60 and UIQ platforms can be observed. For this reason, the view class for each will be discussed separately.

The UIQ application view

In UIQ, views are the primary means of displaying data, and handling commands and other user input events. Views can be configured and their initial commands and controls set in resource files using a number of resource structures, including QIK_VIEW_CONFIGURATION, QIK_VIEW, and QIK_VIEW_PAGE. Each view has a class, derived from the framework class CQikViewBase, which handles construction and activation of the view, command handling, and use of the view's controls.

A view object is constructed in several stages to minimise the startup time of the application. The first stage constructs as little as possible. The static factory function, NewL(), follows the standard Symbian OS two phase construction rules. The function instantiates the object, which calls CQikViewBase’s constructor, and then calls the second phase ConstructL() method, which calls CQikViewBase::BaseConstructL() to register the view with the view server to allow navigation between applications.

The next stage of construction occurs in the ViewConstructL() method which is called the first time the view needs to be activated. For applications which use multiple views, the ViewConstructL() method is valuable because it reduces application start up time by only initialising views which are used immediately the application starts. It also avoids wasting memory, particularly if some views are not be used at all.

ViewConstructL() will fully construct the view by reading the view’s configuration resource structure and initialising its controls, in the case of the filesystem browser, the list box. CFileBrowseBaseView::ViewConstructL() also constructs the engine object, used to scan the filesystem, by calling CRFsEngine::NewL().

The last stage is the initialisation of the view by ViewActivatedL(), which is called each time the view becomes visible. This method can be used to re-populate the view with user data, if necessary. For the filesystem browser, ViewActivatedL() calls ShowRootL() which displays the C and D drives of the phone.

In UIQ, all views are uniquely identified by a view ID (a TVwsViewId object), consisting of the UID of the application and an ID that uniquely identifies the view within the application. The view ID is returned by CQikViewBase::ViewId(), a pure virtual method which is called for each view by CQikAppUi::AddViewL(). All view classes must implement this method.

In UIQ, a command can be located on a softkey, in a toolbar or in a menu, depending on the interaction style of the phone. To allow for this flexibility, commands are defined in a more abstract way, rather than being explicitly coded as particular GUI elements such as menu items. This is usually done in a resource file using the QIK_COMMAND_LIST structure. Commands are associated with views, and so are handled within view classes. This is done by reimplementing the HandleCommandL(CQikCommand& aCommand) method defined by CQikViewBase. This looks something like this:

/** 
Handles all commands in the view. 
Called by the UI framework when a command has been issued. 
The command Ids are defined in an .hrh file. 
*/ 
void CHelloWorldView::HandleCommandL(CQikCommand& aCommand)
    { 
    switch(aCommand.Id()) 
            { 
            // Here you can take care of your view specific commands. 
            /** 
            case EFooCmd: 
                    { 
                    DoSomethingL();; 
                    break; 
                    } 
            */               
            // Go back and exit command will be passed to the CQikViewBase to handle. 
            default: 
                    CQikViewBase::HandleCommandL(aCommand); 
                    break; 
            } 
    }

Note that, in UIQ, applications do not provide an exit command. When you start an application, it remains running and persistent; you do not close it but simply switch away from them to use other applications. It is useful though to provide an exit command in debug builds only, to test that the application exits cleanly without leaking memory. The UIQ implementation of the filesystem browser does this in its r_filebrowser_commands resource.

The S60 application view

The S60 application view derives from CCoeControl and is, in effect, a control which itself owns a list box control which is used to display the filesystem. The application view is fully constructed by static factory function, NewL() which instantiates the object and then calls the second phase ConstructL() method to perform any initialisation which may leave.

void CFileBrowseBaseView::ConstructL(const TRect& aRect)
    {
    CreateWindowL();
        
    iEngine = CRFsEngine::NewL();
    
    SetUpListBoxL();
    ShowRootL();

    SetRect(aRect);
    ActivateL();
    }

The ConstructL() method calls CreateWindowL() to create the views associated window. When the filesystem engine object has been constructed, by calling CRFsEngine::NewL() and the list box created and initialised, ConstructL() calls SetRect() to set the area on the screen that the view will occupy and ActivateL() to indicate that the view is ready to draw itself.

The code used to create and initialise the list box is described in the CFileBrowseBaseView implementation section below.

[Top]


Resources

Many elements of a user interface, for instance dialogs, the button bar and menu panes, generally known as resources, are defined using a Symbian OS-specific resource language rather than directly in C++. Resources are expressed in text files which are then compiled into a compressed binary form using the Symbian resource compiler.

Resources are added to a project by including them within START RESOURCE...END blocks within the project's mmp file, as described in the section on mmp file syntax. The IDEs invokes the resource compiler as needed when you build a project, and you can also compile resource files separately on the command line using the abld resource command. See Building code using the command line for more details.

An advantage of being able to separate the resources from the C++ code is that the resource files can be edited separately, for instance by a localisation team, without any impact on the rest of the program, in particular, there is no need to recompile it.

The resource language syntax is documented in the Symbian OS Library: see Resource file source format.

The table below describes the resource files used in the filesystem browser. It is followed by section which describes in depth how resources are defined in the filebrowse resource file.

Resource files used in the filesystem browser

filebrowseapp.rss

Contains the GUI definitions. See the section below for a discussion of its contents for both S60 and UIQ implementations of the filesystem browser.

filebrowseapp.rsc

The compiled resource file, generated from filebrowseapp.rss by the resource compiler.

filebrowseapp.rsg

The compiler generated resource header which contains the symbolic IDs of every resource defined in filebrowseapp.rss. It is created in epoc32\include\ and can be included in the source files which need to refer to resources.

filebrowse.hrh

Defines the IDs of application-specific commands and controls using C++ syntax. It is included both in filebrowseapp.rss and in various .cpp files, on each platform.

For example, in the S60 implementation, the command IDs (TFileBrowseCommands) are associated in the .rss and .cpp files with the buttons, menu options and soft keys that issue them. The UIQ filesystem browser implementation does not use them because it does not have any command-generating resources.

filebrowseapp.rls

Contains localisation strings which are displayed in the GUI, for instance in dialog titles or in menus. These strings are referred to in the resource file or in code using their symbolic names. Defining the text in its own file, separate from the .rss file makes the task of translating the application into different languages (localisation) much easier.

filebrowseapp_reg.rss

A special type of resource file, called a registration file, which provides the information that allows the application to be displayed in the application launcher. As seen in the section on mmp file syntax, when specifying this file in a project's mmp file, it must always place the compiled resource file into \private\10003A3F\apps (the private use area of the apparc server, which is responsible for application registration).

This file includes the name of the application binary, the MIME types it can handle, and the location of the localisable registration information (built from filebrowseapp_loc.rss). The latter includes the icons and captions. This file is identical for both S60 and UIQ implementations of the filesystem browser.

Its format is documented in the Symbian OS Library, see Application registration information.

filebrowseapp_loc.rss

Contains location specific registration information (such as an application caption). The registration resource file (filebrowseapp_reg.rss) described above contains only non-localisable information and simply includes the location of this compiled resource file.

The _loc-file defines a single structure called LOCALISABLE_APP_INFO which contains the caption and the icon bitmap locations for the application. This file is identical for both S60 and UIQ implementations of the filesystem browser.

avkon.rh, qikon.rh, eikon.rh, appinfo.rh and others.

Standard GUI framework and S60- or UIQ-specific resource headers. These specify the standard resource structs used to define UI elements in the .rss file.

avkon.hrh, uikon.hrh, qikon.hrh, eikon.hrh and others.

Standard GUI framework and S60- or UIQ-specific symbolic IDs, for instance flags and control IDs for the standard controls.

The following diagram summarises how each of the resource files are compiled and combined with other binary files, built from the application source code and bitmaps, to generate an installable application.

The filesystem browser resource files an...


The filesystem browser resource files and the resulting application binaries on compilation.

[Top]


filebrowseapp.rss

Each resource file must include a NAME statement before the first resource definition:

NAME SFSB

The four character long name identifies the resource file, so it must be unique among the resource files used by the application.

The following three unnamed resources are also required:

The rest of the resource file contains resource definitions in any order, each prefixed with RESOURCE to indicate a separate resource structure. Since the filesystem browser application resource files for UIQ and S60 are almost completely different, they will be discussed separately. The only common factor in both resource files are the string resource definitions which hold strings that are used in the C++ source code, for instance, the text used in information messages. These are defined as follows:

RESOURCE TBUF r_filebrowse_driveC { buf = STRING_r_filebrowse_driveC; }
RESOURCE TBUF r_filebrowse_driveD { buf = STRING_r_filebrowse_driveD; }
RESOURCE TBUF r_filebrowse_infonote { buf = STRING_r_information_note; }

The resource strings to be displayed are not hard-coded into the resource file, but defined in the separate localisation header file (filebrowseapp.rls) as follows:

rls_string STRING_r_filebrowse_driveC          "Drive C:"
rls_string STRING_r_filebrowse_driveD          "Drive D:"
rls_string STRING_r_information_note           "Cannot open file"

An rls_string is similar to a C++ #define macro: for example, "Cannot open file" will be substituted in the resource file wherever STRING_r_information_note is referred to. But an rls_string doesn't by itself define resources that you can use in C++ code.

[Top]


filebrowseapp.rss in UIQ

The UIQ platform supports the demands of different phone input and screen modes by using high level, global settings (UI configuration parameters) to define screen layout (landscape or portrait, screen size), touch screen, menu or soft key input and orientation. These UI configuration parameters combine to create the look and feel of different configuration modes. The platform pre-defines five modes: Softkey Style, Pen Style, Softkey Style Small, Softkey Style Touch and Pen Style Landscape. See the UIQ SDK or information on the UIQ website for more information.

In a resource file, the QIK_VIEW_CONFIGURATIONS resource structure is used to define which UI configuration modes an application supports. If a mode is supported, the framework will automatically switch view layout and commands when the window-server changes to that mode. The filesystem browser supports the five modes and, for each, specifies the same view to be displayed in a QIK_VIEW structure, which itself identifies the pages to display using QIK_VIEW_PAGES.

For each page, the content is specified in a container, using the QIK_CONTAINER_SETTINGS resource structure, which can contain an array of controls, each within a QIK_CONTAINER_ITEM_CI_LI structure.

The filesystem browser view consists of a single control, a list box, whose behaviour is defined in the resource file as follows:

// Specifies controls and how they are laid out
RESOURCE QIK_CONTAINER_SETTINGS r_filebrowse_baseview_page_control
  {
        // Specifies layout settings
  layout_manager_type = EQikRowLayoutManager;
  layout_manager = r_row_layout_default;
        // Specifies controls
  controls =
    {
    // A reference to the listbox control
    QIK_CONTAINER_ITEM_CI_LI
      { 
      unique_handle = EFileBrowseListbox;
      type = EQikCtListBox; 
      control = r_filebrowse_baseview_listbox;
      layout_data = r_row_layout_data_fill;
      }
    };
  }

// Layout manager
RESOURCE QIK_ROW_LAYOUT_MANAGER r_row_layout_default
  {
  default_layout_data = QIK_ROW_LAYOUT_DATA {};
  }

// Layout parameters
RESOURCE QIK_ROW_LAYOUT_DATA r_row_layout_data_fill
  {
  vertical_alignment = EQikLayoutVAlignFill;
  vertical_excess_grab_weight = 1;
  }

// Resource that defines a listbox
RESOURCE QIK_LISTBOX r_filebrowse_baseview_listbox
  {
  layouts = { r_filebrowse_baseview_normal_layout_pair };
  }

// Layout for listbox
RESOURCE QIK_LISTBOX_LAYOUT_PAIR r_filebrowse_baseview_normal_layout_pair
  {
  // standard layout
  standard_normal_layout = EQikListBoxIconLine;
  }

For more information about these resource structures, you should consult the appropriate sections of the UIQ SDK.

[Top]


filebrowseapp.rss in S60

The contents of filebrowseapp.rss for the S60 implementation is significantly less complex, as it uses the approach of configuring the list box control mostly in C++ code rather than using the resource file as in the UIQ implementation. The list box is simply identified in the resource file as follows:

RESOURCE LISTBOX r_filebrowse_baseview_listbox
  {
  flags = EAknListBoxSelectionList;
  }

For more information about the LISTBOX resource, see the Uikon Resources reference in the Symbian OS Library and the S60 SDK for information about the different list box types provided by Avkon. All other list box customisation occurs in the code, as described in the CFileBrowseBaseView implementation section below:

In either the S60 or UIQ versions of the application, the filesystem can be navigated by selecting items in the list box, moving back up the directory hierarchy using the first item in the list box.

In the S60 application, the user can eit...


In the S60 application, the user can either navigate up the directory hierarchy using the top 'back' directory, or the right soft key.

Additionally, the S60 application also has a menu which can be invoked using the left soft key, to open a directory or exit the application. The right soft key can be used to navigate back up the directory hierarchy. The resource file structures for this are as follows:

// Define command input and controls (menu and Control Button Array)
RESOURCE EIK_APP_INFO
    {
    menubar=r_filebrowse_menubar;
    cba=R_AVKON_SOFTKEYS_OPTIONS_BACK;     
    }


// Top level menu resource - defines the menu pane used
RESOURCE MENU_BAR r_filebrowse_menubar
    {
    titles=
        {
        MENU_TITLE { menu_pane=r_filebrowse_menu; txt=""; }
        };
    }

// Defines the menu items that comprise a menu pane
RESOURCE MENU_PANE r_filebrowse_menu

    {
    items=
        {
        MENU_ITEM { command=ECmdFileBrowseOpen;  
                    txt = STRING_r_filebrowse_loc_Open; },
        MENU_ITEM { command=EAknCmdExit;  
                    txt = STRING_r_filebrowse_loc_Exit; }
        };
    }

The MENU_PANE resource structure is used to add items to the menu (note that the options are, again, defined using strings from the localisation header file rather than hard-coded into the resource). A command is associated with each option, which is defined in either the filebrowse.hrh example header file (ECmdFileBrowseOpen) or the avkon.hrh system header file (EAknCmdExit). The menu pane is referenced by the MENU_TITLE section of the MENU_BAR resource structure. Note that the title of the menu, which is not used in S60, is left blank.

The commands and labels associated with the soft keys are set up using the cba attribute of the EIK_APP_INFO structure (where cba stands for 'Control Button Array'). In the filesystem browser application, a standard pair is used from avkon.rsg, which sets the left soft key label to "Options" and the right soft key to "Back", which, when pressed, sends a EAknSoftkeyBack command to the HandleCommandL() method of the application UI class.

[Top]


CFileBrowseBaseView implementation

The display for CFileBrowseBaseView is quite straightforward, consisting of a single list box which displays the subdirectories and files of a particular directory in the phone's filesystem. This section will examine some of the more important features of the view code for both the S60 and UIQ implementations. For more information, consult the App framework CONE section of the Symbian OS Library, or the relevant sections of the UIQ or S60 SDKs.

Controls

A control is a rectangular area of the screen that can be drawn and can handle key and pointer events. All controls are derived from CCoeControl. Some are intended as base classes for other controls, while others are intended to be instantiated and used as they are. Application developers can write their own concrete controls, or can use the ones supplied by UIQ or S60.

In the filesystem browser, the application view is a control. It is known as a compound control because it owns another control, the list box, iListBox, which displays files and directories.

The characteristics of the list box can be mostly specified in a resource file, as in the UIQ filesystem browser, or by settings in C++ code as in the S60 implementation. The list box handles the basics of input, such as highlighting the selected item, so you don't need to write code to do this.

In the S60 implementation, the list box is created and initialised by CFileBrowseBaseView::ConstructL() which calls a private method in the view class, SetUpListBoxL(), defined as follows:

void CFileBrowseBaseView::SetUpListBoxL()
    {
    iListBox = new (ELeave) CAknSingleGraphicStyleListBox();
    iListBox->SetContainerWindowL(*this);
    
    TResourceReader reader;
    iEikonEnv->CreateResourceReaderLC(reader, R_FILEBROWSE_BASEVIEW_LISTBOX);

    // Create the list box
    iListBox->ConstructFromResourceL(reader);
    CleanupStack::PopAndDestroy(); // reader
    
    
    // Add this to observe the list box
    iListBox->SetListBoxObserver(this);
    
    SetupListIconsL();
    
    //  Add vertical scroll bars (which are visible when necessary)
    iListBox->CreateScrollBarFrameL(ETrue);
    iListBox->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff, \
    CEikScrollBarFrame::EAuto); 
    }

The list box is created as an object of class CAknSingleGraphicStyleListBox and its container window and observer object (both are the owning view class object) are set by calling SetContainerWindowL() and SetListBoxObserver() respectively upon it. The scroll bars are configured to be invisible for horizontal scroll and automatic for vertical scrolling, which means that they are displayed when there are more entries in the list box than can be displayed on the screen without scrolling.

The icons that the list box will display for a file or folder are created in icon 'slots' in advance by the SetupListIconsL() method (in comparison to the UIQ implementation, which creates an object to represent the icons each time they are required). For more information about list box controls in S60 or UIQ, you should consult the SDK documentation for the appropriate platform, or refer to the Additional resources section of this tutorial.

Control observers

Compound controls normally need to observe their components for events such as when the user scrolls through the contents of a list box or clicks to select an item. MQikListBoxObserver is a UIQ interface designed for handling list box-specific events, while the S60 implementation uses MEikListBoxObserver.

In both cases, the view sets itself as a list box observer by calling:

iListBox->SetListBoxObserver(this);

The list box observer interface specifies a single function called HandleListBoxEventL(), that the view class implements to handle events generated when an item in the list box is selected, either by a pointer tap (in UIQ) or key press selection.

Static()

All controls in a GUI application have an iEikonEnv member, which is a pointer to the GUI environment, CEikonEnv. CEikonEnv provides many useful functions, for instance access to standard fonts, formatting and colours, a connection with the file server, and the ability to display information messages. Objects that are not controls can get a GUI environment pointer by calling CEikonEnv::Static().

Drawing

Views may be redrawn either by the system, or by the application. System-initiated redraws occur when the window is first created and when another window is moved away, exposing the control. Application-initiated redraws are needed when the application's data has changed, for instance, in the filesystem browser example, when an item is added to a list box.

CAknInformationNote and InfoWinL()

The S60 filesystem browser uses an information note to indicate to the user that they cannot open a file. The CAknInformationNote class is a pre-defined dialog class which requires no definition in the resource file. It is used as follows

HBufC* noteBuf = StringLoader::LoadLC(R_FILEBROWSE_INFONOTE);       
CAknInformationNote* note = new (ELeave) CAknInformationNote();
note->ExecuteLD(*noteBuf);
CleanupStack::PopAndDestroy(noteBuf)

This code follows a standard pattern:

The CAknInformationNote dialog which ind...


The CAknInformationNote dialog which indicates that the file cannot be opened in the S60 version of the filesystem browser.

The UIQ filesystem browser uses a stock dialog common to all the Uikon platforms to indicate that a file cannot be opened. The CEikonEnv::InfoWinL() call can display one or two lines of text in a waiting information dialog which the user must dismiss before continuing to use the application.

The information dialog resulting from a...


The information dialog resulting from a call CEikonEnv::InfoWinL() to indicate that the file cannot be opened in the UIQ version of the filesystem browser.

[Top]


Application icons and captions

The application has an icon and a caption associated with it in both the main 'Applications' screen (used to navigate the installed applications available on the smartphone) and when the application itself is running. The application caption and compiled icon bitmap file (.mbm) are both specified in the localisable resource file described in the Resources section.

If no images are provided, or the size is invalid, a default icon is substituted. This is the case in the filesystem browser application, which only supplies icons for the file and folder images displayed within the application.

The "FileBrowse" caption displ...


The "FileBrowse" caption displayed for the filesystem browser and the default application icon for S60 (top) and UIQ (bottom).

Each icon actually consists of two bmp files, one for the image itself and the other a mask for the bitmap. The mask is used to give a transparent effect; for every black pixel in the mask, the corresponding image pixel is displayed. For any other coloured mask pixel, the equivalent image pixel is not displayed, which results in image transparency. For the filesystem browser, the same mask image is used for both the file and folder icons.

Icon bitmaps are typically stored in a project's gfx (S60) or images (UIQ) directory. They should be specified in the project definition file, as described in the mmp file syntax section.

For more information see the Defining application icons, captions and properties section of the Symbian OS Library.

[Top]


The filesystem browser engine class and console test code

The filesystem browser engine class, CRFsEngine, is a very simple example to illustrate the use of generic engine code with platform-specific application UI code. The class is defined (in rfsengine.h) and implemented (in rfsengine.cpp) by both S60 and UIQ projects but, in fact, the files are identical and a single copy could be shared by both implementations. If engine code is more complex, it is often desirable to implement it as a separate dll which each version of the filesystem browser application can link against. However, in this case, the class is simply a wrapper around access to the Symbian OS file server, through the file server session class RFs. The class wraps a call to RFs::GetDir() to get the contents of a directory.

For more information about using the Symbian OS filesystem, consult the File Server Client Side documentation in the Symbian OS Library.

The Symbian OS console can be used to test application engine code, even before the UI which uses it has been written. The filesystem browser example includes test code for the CRFsEngine class, under the \test directory. The .mmp file for the console test is as follows:

TARGET          FileBrowseTest.exe
TARGETTYPE      exe
UID             0

SOURCEPATH      .
SOURCE          FileBrowseTest.cpp

// Include the code for CRFsEngine which will be tested
SOURCEPATH      ..\src
SOURCE          rfsengine.cpp

USERINCLUDE     .
SYSTEMINCLUDE   \epoc32\include ..\inc

LIBRARY         euser.lib efsrv.lib

There are several differences between this file and the .mmp file for a typical GUI application:

The implementation of the E32Main() entry point of the executable is as follows. The numbered lines are explained in more detail below.

GLDEF_C TInt E32Main() // 1
    {
    __UHEAP_MARK; // 2

    // Create the cleanup stack
    CTrapCleanup* cleanup = CTrapCleanup::New(); // 3
    TRAPD(result, CallExampleL()); // 4
    __ASSERT_ALWAYS(result==KErrNone, User::Panic(KFileBrowseTest, result)); // 5
    
    // Destroy the cleanup stack
    delete cleanup; // 3  
    
    __UHEAP_MARKEND; // 2
    return 0; 
    }

The code for CallExampleL() is as follows. It creates a test console using the CConsoleBase class which is defined in e32cons.h:

LOCAL_C void CallExampleL() 
    {
    console = Console::NewL(KFileBrowseTest, TSize(KConsFullScreen, KConsFullScreen));
    CleanupStack::PushL(console);

    RunTestsL(); // Runs the tests on class CRFsEngine
    
    CleanupStack::PopAndDestroy(console); // close and clean up the console 
    }

The code shown is "boiler plate code" which any test console can use. Test code for a class or library should then be implemented within the RunTestsL() method, as you can see within the FileBrowseTest.cpp file for the filesystem browser engine's test code.