org.symbian.tools.wrttools.doc.WRTKit/html/WRTKit_RSS_Reader_user_interface-GUID-1083a0c4-a953-4b6e-a4d0-45a031e51c35.html
changeset 230 7848c135d915
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.doc.WRTKit/html/WRTKit_RSS_Reader_user_interface-GUID-1083a0c4-a953-4b6e-a4d0-45a031e51c35.html	Fri Mar 05 19:31:41 2010 -0800
@@ -0,0 +1,631 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html lang="en" xml:lang="en">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
+<meta name="copyright" content="(C) Copyright 2005" />
+<meta name="DC.rights.owner" content="(C) Copyright 2005" />
+<meta content="concept" name="DC.Type" />
+<meta name="DC.Title" content="RSS Reader user interface" />
+<meta scheme="URI" name="DC.Relation" content="WRTKit_RSS_Reader_Tutorial-GUID-678d197f-c7b0-4e5e-85e2-f8549c75bbe8.html" />
+<meta content="XHTML" name="DC.Format" />
+<meta content="GUID-1083A0C4-A953-4B6E-A4D0-45A031E51C35" name="DC.Identifier" />
+<meta content="en" name="DC.Language" />
+<link href="commonltr.css" type="text/css" rel="stylesheet" />
+<title>
+RSS Reader user interface</title>
+</head>
+<body id="GUID-1083A0C4-A953-4B6E-A4D0-45A031E51C35"><a name="GUID-1083A0C4-A953-4B6E-A4D0-45A031E51C35"><!-- --></a>
+
+
+
+    <h1 class="topictitle1">
+RSS Reader user interface</h1>
+
+    <div>
+
+        <div class="section"><h2 class="sectiontitle">
+Preferences</h2>
+
+            
+            <p>
+
+                Now it's time to write some code so let's open up RSSReader.js. The first thing
+                we will do is implement the init() function that gets called when the widget has
+                loaded. We will begin by setting up the timer-based updating mechanism for the
+                feed updates by declaring four variables that we'll need for that:
+            </p>
+
+<pre>
+
+// Feed update timer identifier.
+var updateTimerId = null;
+
+// Feed URL and update frequency (in milliseconds; -1 if no auto update).
+var feedURL = null;
+var feedUpdateFrequency = -1;
+
+// Next scheduled update time; -1 if never.
+var feedUpdateTime = -1;
+</pre>
+
+            <p>
+
+                We know that we'll need to get some working values for the feed URL and feed update
+                frequency variables and we know that they will be configurable values so let's write
+                the code for this:
+            </p>
+
+<pre>
+
+// Called from the onload event handler to initialize the widget.
+function init() {
+    // load preferences
+    loadPreferences();
+
+    // start feed update timer (called once every second)
+    updateTimerId = setInterval(updateFeedTimerFunc, 1000);
+}
+
+// Loads widget preferences.
+function loadPreferences() {
+    if (window.widget) {
+        // read feed URL and update frequency from the widget settings
+        feedURL = widget.preferenceForKey("FeedURL");
+        var feedUpdateFrequencyStr = widget.preferenceForKey("FeedUpdateFrequency");
+        feedUpdateFrequency = (feedUpdateFrequencyStr == null) ? -1 : parseInt(feedUpdateFrequencyStr);
+    }
+}
+
+// Timer function for feed updates - called once every second.
+function updateFeedTimerFunc() {
+    
+}
+</pre>
+
+            <p>
+
+                The init() function does two things at this point. First it calls our newly created
+                loadPreferences() function that takes care of reading some values for the feedURL
+                and feedUpdateFrequency variables. After this it starts up an interval timer that
+                calls updateFeedTimerFunc() once every second. The function doesn't do anything
+                yet but we'll get to that in a bit. The timer identifier is stored in the updateTimerId 
+                variable. If no configuration is found, feedURL will have a value of null and
+                feedUpdateFrequency will have a value of -1. Note that all preferences are stored
+                as strings so we need to parse the string value to an integer. Also note that we
+                wrapped the preference loading code in an if-clause that checks if we are in the
+                S60 Web Runtime environment. This allows us to run the code in a PC browser without
+                getting an error message about the widget -related methods that don't exist outside
+                of the S60 Web Runtime.
+            </p>
+
+            <p>
+
+                While we're on the topic of preferences we'll write a save counterpart for the
+                loadPreferences() function.
+            </p>
+
+<pre>
+
+// Saves widget preferences.
+function savePreferences() {
+    if (window.widget) {
+        // save settings in widget preferences store
+        widget.setPreferenceForKey(feedURL, "FeedURL");
+        widget.setPreferenceForKey(feedUpdateFrequency.toString(), "FeedUpdateFrequency");
+    }
+}
+</pre>
+
+            <p>
+
+                We're now almost done with the timer mechanism that will update the RSS feed but
+                we are missing one thing: a way to manually schedule an immediate update of the
+                feeds. Let's add a flag that we can use to track if an update was triggered
+                automatically or manually:
+            </p>
+
+<pre>
+
+// Flag that tracks if a feed update is commanded or automatic.
+var feedUpdateCommanded = false;
+</pre>
+
+            <p>
+
+                And now let's add a function that can be called to schedule an immediate update:
+            </p>
+
+<pre>
+
+// Schedules an immediate feed update.
+function updateFeed() {
+    feedUpdateTime = 0;
+    feedUpdateCommanded = true;
+}
+</pre>
+
+            <p>
+
+                The function just sets the next update time to 0 so that the next time the
+                timer function runs it will notice that it's time to schedule a feed update.
+                The function also sets the feedUpdateCommanded flag to true so that the
+                updating mechanism will know that this was a manual update.
+            </p>
+
+        </div>
+
+        <div class="section"><h2 class="sectiontitle">
+Creating the user interface</h2>
+
+            
+            <p>
+
+                Now let's create the user interface. We'll start by declaring three variables
+                where we will hold references to the user interface manager, main view and settings
+                view. Note that these should be outside the init() function.
+            </p>
+
+<pre>
+
+// References to the WRTKit user interface manager and views.
+var uiManager;
+var mainView;
+var settingsView;
+</pre>
+
+            <p>
+
+                We want our user interface to be in tab navigation mode and we want the softkey
+                bar to be visible. The code for this is the same as in the Hello World example.
+                Next we create the user interface manager, main view and settings view by adding
+                the following code to the init() function:
+            </p>
+
+<pre>
+
+if (window.widget) {
+    // set tab-navigation mode and show softkeys
+    widget.setNavigationEnabled(false);
+    menu.showSoftkeys();
+}
+
+// create UI manager
+uiManager = new UIManager();
+
+// create main view
+mainView = new ListView();
+
+// create settings view
+settingsView = new ListView(null, "Settings");
+</pre>
+
+            <p>
+
+                You might have noticed that we didn't supply any arguments to the ListView constructor
+                when the main view was created. The reason for this is that we will set the view caption
+                dynamically depending on the news feed we are following. And since we don't need the
+                unique identifier for the main view we don't even have to pass null to it. Remember: in
+                JavaScript if you don't pass an argument it will be undefined, which is equal to null.
+            </p>
+
+            <p>
+
+                The main view will not get populated with controls until we have some news feed items
+                to display, but we can go ahead and create the settings view. Again, we will first
+                declare variables to hold references to the controls and again, they should be declared
+                outside the init() method so that they are accessible elsewhere in the widget code:
+            </p>
+
+<pre>
+
+// Reference to settings controls.
+var feedSelection;
+var feedUpdateFrequencySelection;
+var settingsSaveButton;
+var settingsCancelButton;
+</pre>
+
+            <p>
+
+                Now that we have variables to receive the control references we can go ahead and create
+                them and add them to the settings view. We will create a SelectionMenu control to let
+                the user select the RSS feed to show, a SelectionList control to allow selection of
+                the feed update frequency and two form buttons: "Save" and "Cancel". The SelectionMenu
+                and SelectionList controls are identical from a developer's point of view but look
+                very different. The SelectionMenu takes up very little space in the view but when clicked
+                it pops up a list of options that the user can select from. This is good if theres a lot
+                of options and we don't want to use up a lot of space from the view. The SelectionList
+                is the exact opposite and shows all the options right there in the view. This is good
+                if the widget is in pointer navigation mode or if there are very few options.
+            </p>
+
+            <p>
+
+                Options for the SelectionMenu and SelectionList controls are defined as an array of
+                JavaScript objects that have two properties: value and text. The value property contains
+                the actual data of the option and the text property contains the string to display to
+                the user. Before we create the controls we need to create the option arrays for the
+                RSS feed options and update frequency options. These will simply be hard coded arrays
+                that are defined using JavaScript object notation (JSON) and we'll place them at the top
+                of the widget code so that they can be easily customized later on:
+            </p>
+
+<pre>
+
+// Feed source options.
+var feedOptions = [
+    { value: "http://www.womworld.com/nseries/feed/", text: "Nseries WOM World" },
+    { value: "http://feeds.feedburner.com/N958GB", text: "N95 8GB News" },
+    { value: "http://feeds.feedburner.com/N95", text: "N95 News" },
+    { value: "http://feeds.feedburner.com/N93", text: "N93 News" },
+    { value: "http://feeds.feedburner.com/N91", text: "N91 News" },
+    { value: "http://feeds.feedburner.com/N82", text: "N82 News" },
+    { value: "http://feeds.feedburner.com/N81", text: "N81 News" },
+    { value: "http://feeds.feedburner.com/N810", text: "N810 News" },
+    { value: "http://feeds.feedburner.com/N800", text: "N800 News" },
+    { value: "http://feeds.feedburner.com/N76", text: "N76 News" }
+];
+
+// Feed update frequency.
+var updateFrequencyOptions = [
+    { value: -1, text: "never" },
+    { value: (1000 * 60 * 5), text: "every 5 min" },
+    { value: (1000 * 60 * 15), text: "every 15 min" },
+    { value: (1000 * 60 * 60), text: "every 60 min" },
+];
+</pre>
+
+            <p>
+
+                The RSS feeds are all from the Nokia Nseries WOM World website but you could put any
+                RSS feed URLs here that you like. However it is probably a good idea to stick with
+                these for now since we know that they work. Later when the widget is completed you can try
+                with other feeds. Note how the value is a URL but the text to display is a human readable
+                name for the RSS feeds.
+            </p>
+
+            <p>
+
+                The update frequency options are defined in milliseconds and there is one option with a
+                magic value of -1 for "never". Here too you'll notice the difference between the value
+                (an integer in this case) and the human readable text.
+            </p>
+
+            <p>
+
+                Now that we have the option arrays we can create the controls for the settings view by
+                adding the following code to the init() function:
+            </p>
+
+<pre>
+
+    // feed selection control
+    feedSelection = new SelectionMenu(null, "Select feed", feedOptions);
+    settingsView.addControl(feedSelection);
+    
+    // feed update frequency selection control
+    feedUpdateFrequencySelection = new SelectionList(null, "Check for updates", updateFrequencyOptions);
+    settingsView.addControl(feedUpdateFrequencySelection);
+    
+    // save settings button
+    settingsSaveButton = new FormButton(null, "Save");
+    settingsView.addControl(settingsSaveButton);
+    
+    // cancel settings button
+    settingsCancelButton = new FormButton(null, "Cancel");
+    settingsView.addControl(settingsCancelButton);
+</pre>
+
+            <p>
+
+                The two first arguments for the SelectionMenu and SelectionList constructors are the
+                same as for most other controls: a unique identifier and a control caption. The third 
+                argument points to the array of options that the controls should display. You could
+                also have omitted that argument from the constructor and instead called setOptions()
+                later on, but why do it in two steps when you can do it in one?
+            </p>
+
+            <p>
+
+                The user interface views are now done (minus the actual news items to display in the main
+                view of course) but we still have some things to implement for the user interface. The
+                buttons in the settings view don't do anything yet so let's fix that.
+            </p>
+
+            <p>
+
+                The cancel button should take the user to the main view and the save button should
+                start using the newly configured settings, save them, and then go to the main view.
+                Our plan to implement this is to create a function for showing the main view, called
+                "showMainView()", a similar function for showing the settings view that we will call
+                "showSettings()", and a function that will be called when the save button is clicked
+                called "saveSettingsClicked()". We will then add event listeners to the two buttons
+                so that clicking the save button will result in a call to the saveSettingsClicked()
+                function and clicking the cancel button will result in a call to the showMainView()
+                function. Let's create empty placeholders for the functions first:
+            </p>
+
+<pre>
+
+// Callback for settings view save button.
+function saveSettingsClicked() {
+}
+
+// Show main view.
+function showMainView() {
+}
+
+// Show settings view.
+function showSettings() {
+}
+</pre>
+
+            <p>
+
+                Now that we have those functions let's register the event listeners to the form
+                buttons by adding these two lines of code immediately after where the buttons
+                are created:
+            </p>
+
+<pre>
+
+settingsSaveButton.addEventListener("ActionPerformed", saveSettingsClicked);
+settingsCancelButton.addEventListener("ActionPerformed", showMainView);
+</pre>
+
+            <p>
+
+                We are now ready to implement the functions to show the main view, settings view
+                and react to the save button click. But we won't implement those just yet because
+                we want to do one more thing before that. Let's create the custom Options menu.
+                The Options menu isn't using any WRTKit code but rather the standard S60 Web Runtime
+                widget menu functionality. We'll have two menu items: "Refresh" to schedule an immediate
+                news feed update and "Settings" to go to the settings view. The Web Runtime Options
+                menu works so that a callback function gets called when the user selections an Options
+                menu item. In order to recognize which menu item was selected we'll need a unique
+                identifier for each of them. Let's create those:
+            </p>
+
+<pre>
+
+// Constants for menu item identifiers.
+var MENU_ITEM_SETTINGS = 0;
+var MENU_ITEM_REFRESH = 1;
+</pre>
+
+            <p>
+
+                Next we'll create the callback function that will be called when the menu items are
+                selected:
+            </p>
+
+<pre>
+
+// Callback for when menu items are selected.
+function menuItemSelected(id) {
+    switch (id) {
+        case MENU_ITEM_SETTINGS:
+            showSettings();
+            break;
+        case MENU_ITEM_REFRESH:
+            updateFeed();
+            break;
+    }
+}
+</pre>
+
+            <p>
+
+                This function gets the menu item identifier passed to it when the user selects
+                a menu item. The switch-case will branch off to call showSettings() if the "Settings"
+                menu item was selected and updateFeed() if the "Refresh" menu item was selected.
+                But we haven't actually created the menu yet so let's do that by adding this to the
+                init() function right after where we show the softkey bar. We want to have this piece
+                of code inside the if-clause that checks if we're in the S60 Web Runtime because we
+                don't have the Options menu functionality on a PC browser.
+            </p>
+
+<pre>
+
+// create menu
+var settingsMenuItem = new MenuItem("Settings", MENU_ITEM_SETTINGS);
+settingsMenuItem.onSelect = menuItemSelected;
+menu.append(settingsMenuItem);
+var refreshMenuItem = new MenuItem("Refresh", MENU_ITEM_REFRESH);
+refreshMenuItem.onSelect = menuItemSelected;
+menu.append(refreshMenuItem);
+</pre>
+
+            <p>
+
+                The code simply creates two MenuItem objects, gives them a label and a unique
+                identifier, specifies the callback function to call when selected and then adds
+                them to the Options menu. The menu object is a global object that automatically
+                exists in the S60 Web Runtime.
+            </p>
+
+            <p>
+
+                Now let's return to the three functions we created but didn't implement. We will
+                start with the showMainMenu() function. This function should perform all necessary
+                steps to show the main menu. This includes the following: setting a caption for
+                the main view, setting the right softkey to be the default "Exit" label and 
+                functionality, and then actually showing the main view by calling setView() in the
+                user interface manager. We'll get the main view caption by checking which of the
+                RSS feeds the user has currently selected. We do this by looping through the feed
+                options and looking for the feed URL that is currently in use. If we can't find one
+                we'll default to "RSS Reader".
+            </p>
+
+<pre>
+
+// Show main view.
+function showMainView() {
+    // set main view caption from feed name
+    var feedName = null;
+    for (var i = 0; i &lt; feedOptions.length; i++) {
+        if (feedOptions[i].value == feedURL) {
+            feedName = feedOptions[i].text;
+            break;
+        }
+    }
+    var mainViewCaption = (feedName == null) ? "RSS Reader" : feedName;
+    mainView.setCaption(mainViewCaption);
+    
+    // set right softkey to "exit"
+    if (window.widget) {
+        menu.setRightSoftkeyLabel("", null);
+    }
+    
+    // show the main view
+    uiManager.setView(mainView);
+}
+</pre>
+
+            <p>
+
+                When the settings view is shown we first want to place the controls into
+                a state that reflects the current configuration. In other words we want
+                the currently configured feed to be the one that's selected in the feed selection
+                control. In the same way the currently configured feed update frequency should
+                be selected in the feed update frequency selection control. There is a very
+                convenient method available for this called getOptionForValue(). Using that
+                function we can pass the currently configured feed URL and feed update frequency
+                and get back the options object that corresponds to the configuration value.
+                After this we can use that options object when we call setSelected() to select
+                that option.
+            </p>
+
+            <p>
+
+                If there is no valid configuration then we disable the cancel button and make
+                the right softkey be "Exit". This is useful when the widget is started for the
+                first time so that user has to enter a valid configuration before starting to
+                use the widget. If there is a valid configuration we set the right softkey to
+                "Cancel" and make it call the showMainView() function that we just created. The
+                "Cancel" button will also be enabled.
+            </p>
+
+            <p>
+
+                Finally we'll switch to the settings view by calling setView() in the user 
+                interface manager.
+            </p>
+
+<pre>
+
+// Show settings view.
+function showSettings() {
+    // URL (use first available feed if null)
+    var feedOption = (feedURL == null) ? feedOptions[0] : feedSelection.getOptionForValue(feedURL);
+    feedSelection.setSelected(feedOption);
+    
+    // frequency
+    var feedUpdateFrequencyOption = feedUpdateFrequencySelection.getOptionForValue(feedUpdateFrequency);
+    feedUpdateFrequencySelection.setSelected(feedUpdateFrequencyOption);
+    
+    if (feedURL == null) {
+        // no valid configuration
+        // disable cancel button - set right softkey to "exit"
+        settingsCancelButton.setEnabled(false);
+        if (window.widget) {
+            menu.setRightSoftkeyLabel("", null);
+        }
+    } else {
+        // we have a valid configuration
+        // enable cancel button - set right softkey to "cancel"
+        settingsCancelButton.setEnabled(true);
+        if (window.widget) {
+            menu.setRightSoftkeyLabel("Cancel", showMainView);
+        }
+    }
+    
+    // show the settings view
+    uiManager.setView(settingsView);
+}
+</pre>
+
+            <p>
+
+                The third function to implement is the callback for when the save button is clicked.
+                First we take the selected options from the feed selection and feed update frequency
+                selection controls. Those selected values are copied to the feedURL and feedUpdateFrequency
+                variables that are used by the widget during runtime. Next the settings are saved using
+                the savePreferences() function that we implemented earlier. And finally we call the
+                showMainView() method so that the widget shows the main view with the news items.
+            </p>
+
+<pre>
+
+// Callback for settings view save button.
+function saveSettingsClicked() {
+    // update feed URL
+    var selectedFeed = feedSelection.getSelected();
+    feedURL = (selectedFeed != null) ? selectedFeed.value : null;
+    
+    // update frequency
+    var selectedFrequency = feedUpdateFrequencySelection.getSelected();
+    feedUpdateFrequency = (selectedFrequency != null) ? selectedFrequency.value : -1;
+    
+    // save preferences
+    savePreferences();
+    
+    // return to main view
+    showMainView();
+}
+</pre>
+
+            <p>
+
+                The user interface is now nearly done but the init() function needs one more
+                thing. We need to show a view after the function is done initializing
+                the widget. The view to show depeneds on whether we have a valid
+                configuration or not. If we do then we'll show the main view. If not then
+                we'll show the settings view. Let's add this code to the end of the init()
+                function, just before we start the update timer:
+            </p>
+
+<pre>
+
+// display the main view if a feed has been configured
+// otherwise show the settings view
+if (feedURL != null) {
+    showMainView();
+    updateFeed();
+} else {
+    showSettings();
+}
+</pre>
+
+            <p>
+
+                You can now go ahead and try out the widget either on a handset, emulator
+                or in a PC browser. You should have a working settings view and when clicking
+                the save button in the settings view you should end up in the main view. From
+                there you can go to the settings by selecting "Settings" in the Options menu.
+            </p>
+
+            <p>
+
+                There are no news items to show yet but that's our next task.
+            </p>
+
+            <div class="fignone" id="GUID-1083A0C4-A953-4B6E-A4D0-45A031E51C35__GUID-31A7B99C-BD6B-42E2-8430-631D210FAFC0"><a name="GUID-1083A0C4-A953-4B6E-A4D0-45A031E51C35__GUID-31A7B99C-BD6B-42E2-8430-631D210FAFC0"><!-- --></a><span class="figcap">Figure 1. 
+RSS Reader settings view</span>
+
+                
+                <br /><img src="RSS_Reader_Settings_Screenshot_1.png" /><br />
+            </div>
+
+        </div>
+
+    </div>
+
+<div>
+<div class="familylinks">
+<div class="parentlink"><strong>Parent topic:</strong> <a href="WRTKit_RSS_Reader_Tutorial-GUID-678d197f-c7b0-4e5e-85e2-f8549c75bbe8.html">RSS Reader</a></div>
+</div>
+</div>
+
+</body>
+</html>
\ No newline at end of file