Training
Certifications
Books
Special Offers
Community




 
Programming Microsoft® Windows® CE, Second Edition
Author Douglas Boling
Pages 1040
Disk 2 Companion CD(s)
Level All Levels
Published 06/13/2001
ISBN 9780735614437
ISBN-10 0-7356-1443-1
Price(USD) $59.99
To see this book's discounted price, select a reseller below.
 

More Information

About the Book
Table of Contents
Sample Chapter
Index
Related Series
Related Books
About the Author

Support: Book & CD

Rate this book
Barnes Noble Amazon Quantum Books

 


Chapter 15: Extending the Pocket PC continued


Writing a Custom Today Screen Item

The Today screen is the home page of the Pocket PC. It’s automatically displayed after the system isn’t used for a predetermined period of time. It contains a snapshot of the relevant data from the applications bundled with the Pocket PC. By using a simple DLL, you can extend the Today screen to allow other applications to summarize their data or to allow stand-alone Today screen inserts that provide data only through the Today screen. Figure 15-1 shows the Today screen with five items: the Today title bar, the Owner Info item, the Tasks item, the Inbox item, and the Calendar item.

Today screen items are implemented as simple Windows CE DLLs with two predefined entry points. The system finds the extensions by looking under a specific registry key. It then loads the DLL, asks the item its desired height, and asks it to display its data. The Today Control Panel applet allows users to selectively enable and disable individual items as well as set the order of the items on the Today screen. The user can also configure an individual item through the Today Control Panel applet. When the user selects an item from a list of all the Today screen items and taps the Options button, the item’s DLL is loaded and a dialog box is created by using a dialog box procedure exported from the DLL. This dialog box is created using resources stored in the DLL.

Click to view graphic
Click to view graphic

Figure 15-1 The Pocket PC Today screen

Creating a Today Screen Item DLL

The requirements for a Today screen item DLL start with an exported entry point, InitializeCustomItem, which must be exported as ordinal 240. The DLL can also optionally support a configuration dialog box. If it does, the DLL must export another entry point, CustomItemOptionsDlgProc, at ordinal 241, which is used as the dialog box procedure for the options dialog. In addition, the resource for the options dialog must be included in the DLL’s resource and have a resource ID of ID_TODAY_CUSTOM.

The InitializeCustomItem function is prototyped as

HWND APIENTRY InitializeCustomItem (TODAYLISTITEM *ptli, HWND hwndParent);

The first parameter is a pointer to a TODAYLISTITEM structure; the second parameter is the handle of a window that will be the parent of the item window created by the extension. Because the TODAYLISTITEM structure is used throughout the Today screen interface, this is as good a place as any to describe it. Many of the fields in this structure might not be useful or even relevant in this call. However, the structure tends to be passed back to the DLL on almost every call, so most of the fields are used at some time in the life of the DLL. The structure is defined as

typedef struct _TODAYLISTITEM {
    TCHAR szName[MAX_ITEMNAME];
    TODAYLISTITEMTYPE tlit;
    DWORD dwOrder;
    DWORD cyp;
    BOOL fEnabled;
    BOOL fOptions;
    DWORD grfFlags;
    TCHAR szDLLPath[MAX_PATH];
    HINSTANCE hinstDLL;
    HWND hwndCustom;
    BOOL fSizeOnDraw;
    BYTE *prgbCachedData;
    DWORD cbCachedData;
} TODAYLISTITEM;

When InitializeCustomItem is called, the szName field is filled with the name of the registry key that identified the item. This name is handy for finding the item’s registry key to retrieve custom data. The second field is tlit, a TODAYLISTITEMTYPE enumeration that defines the type of extension. For custom extensions this field will always be tlitCustom. The dwOrder field will be set to the order index of this item. The cyp field contains the height of the item in pixels. Items are ordered from the lowest to the highest value starting at the top of the Today screen. The user controls the order through the Control Panel applet. For most situations, an extension’s order in the Today window shouldn’t affect the extension’s behavior.

The fEnabled field indicates whether the user has enabled your Today item in the Control Panel. This field should be queried when InitializeCustomItem is called; if it is 0, you should return immediately with a return code of 0. The fOptions flag reflects whether the Today item has an options dialog. This flag is taken from the registry entry for this item.

Let’s skip the grfFlags field for a moment. The szDLLPath field contains the filename of the DLL that contains the code for the item. The hinstDLL field is the DLL’s instance handle. The hwndCustom field will contain the handle of the item’s child window when this structure is passed after the item’s child window has been created. The Today screen item manager uses the fSizeOnDraw field internally.

The last two fields, prgbCachedData and cbCachedData, along with the grfFlags field, allow the DLL to store, or cache, custom data about the state of its window and the data it is displaying. The goal here is to prevent the item from having to query a file or database every time the Today screen is asked to repaint itself. The grfFlags field can be set to anything the DLL requires. Likewise, if the DLL needs to store additional data, a memory block can be allocated. A pointer to the memory block is saved in prgbCachedData, and the size of the memory block is saved in cbCachedData. Since these values are passed back to the DLL on a regular basis, these fields free the DLL from having to store data internally in statically defined structures.

Creating the Item Window

When InitializeCustomItem is called, the DLL should create its child window that will display the data for that item. The window should be a child window with its parent set to the window handle passed in the hwndParent parameter. The function should return the handle to the child window if the initialization was successful, or 0 otherwise.

Of course, to create a window, you will first need to register a class for that window. The class registration can take place either during the processing of the InitializeCustomItem call or during the PROCESS_ATTACH notification to DllMain when the DLL is loaded. If the registration is performed during the InitializeCustomItem call, be sure not to return failure from the function if the call to RegisterClass fails. Because InitializeCustomItem is called more than once, the second call to register the class will fail if the DLL attempts to repeat the class registration. The DLL should also be designed to unregister the window class when the DLL is unloaded. This design feature is quite helpful for debugging purposes, when the DLL will change as the code develops.

The Item Window

Once the item’s window is created, the Today screen will send a custom message, WM_TODAYCUSTOM_QUERYREFRESHCACHE, to the child window. When the message is sent, the wParam parameter points to the TODAYLISTITEM structure that was passed in the call to InitializeCustomItem. The message is sent to ask the item if the data it is presenting to the user has changed and therefore the window needs updating. If so, the window should set the cyp field of the TODAYLISTITEM structure to the height in pixels for the item window. The window should return TRUE for the message. If no update is necessary, the window should respond to the message with FALSE. It is important that the item window return TRUE only when necessary, since returning TRUE causes the Today screen to repaint itself. Having this happen too often—especially when nothing on the screen changes—distracts the user and wastes power.

The item shouldn’t draw in its window during the handling of the WM_TODAYCUSTOM_QUERYREFRESHCACHE message. If the data changes and the item returns TRUE, the item’s window will be invalidated by the item manager, causing a WM_PAINT message to be sent to the item window, which is where the window should be redrawn.

The WM_TODAYCUSTOM_QUERYREFRESHCACHE message is sent to the item’s window every few seconds, allowing the item to check whether it needs to modify the currently displayed data. Since the item has a chance to modify the cyp field, this is also the place where the item can ask to be resized to a taller or shorter window. The width of the window will be the full width of the Pocket PC screen minus the width of the scroll bar if present.

Interacting with the User

The custom item interacts with the user by painting its data onto its window in response to WM_PAINT messages. Because the custom item is a window, it also receives any mouse messages. Given that the user interface guidelines recommend a single click for most actions, the typical thing to do is monitor for a WM_LBUTTONUP event and provide a default action. For example, the item might launch the application that can edit the data the item shows.

Because the item is simply a child window of the Today screen, it can do almost anything a window can do, with these limitations: The Today screen controls the size and position of the item child window, so the item shouldn’t try to move or size itself. Also, the Today screen is designed to scroll if more items are being displayed than can fit on the screen. Because of this feature, the item manager can move your child window at any time.

Unloading the Custom Item

When the Today screen item manager needs to completely refresh the items on the Today screen, it notifies each window by sending a WM_TODAYCUSTOM_CLEARCACHE message. Here again, the wParam parameter points to the item’s TODAYLISTITEM structure, allowing the individual items to free the memory they have allocated during the life of the item. Generally, this means freeing the data block pointed to by the prgbCachedData field if the item had previously allocated such a block of data.

The Options Dialog

Today items must implement their options dialog in a rather strange way. The DLL doesn’t simply export a function that the Today item manager could call to instruct the item to display an options dialog. Instead, the DLL is required to export a specific function, the Options dialog box procedure, and provide in its resource block a dialog box template with a specific ID number. With a pointer to a dialog procedure and a dialog template, the item manager can call CreateDialog itself.

The dialog box procedure provided by the item should conform to Pocket PC user interface guidelines and call SHInitDialog to make itself full screen. In addition, the documentation suggests that the Options dialog box be written to look like the Today screen Control Panel applet, with blue header text and a separator line above whatever dialog controls you see fit to use. The example program at the end of this section has an Options dialog box that conforms to these suggestions. The configuration data should be stored in the registry so that the item window can query it when the Today screen loads the item.

Registering the Custom Item

The Today screen locates the custom items by looking in the registry for a list of items. The registry key that contains the list is [HKEY_LOCAL_MACHINE]\Software\Microsoft\Today\Items. Each custom Today screen item should create a subkey under the key listed above. This subkey name will be the name shown to the user in the Today screen configuration dialog, so it must be localized for the appropriate language. Under the item’s subkey, a number of values must be set. The values are

  • Name   String value containing the name of the item.
  • DLL  String value containing the fully specified path name of the DLL implementing the item.
  • Flags  User-defined DWORD value returned in the grfFlags field of TODAYLISTITEM.
  • Options  DWORD value set to 1 if the item supports an Options dialog box.
  • Enabled  DWORD value set to 1 if the item is enabled.
  • Type   Custom items must set this DWORD value to 4.

The Today screen looks at these registry entries when it loads the items on the Today screen, which happens when the system boots and when the user closes the Today screen Control Panel applet.

Debugging a Custom Item

One of the problems with developing a Today screen item is how to force the Today screen to unload a custom item so that a developer can download a revised copy of that item. When the Today screen starts, it loads all the DLLs listed under the Items key previously described. The DLLs remain loaded even if the user doesn’t enable them. It’s difficult to update a registered Today screen item because a DLL can’t be overwritten until the Today screen unloads that DLL.

In my experience, the best way to force the Today screen to unload an item is to open a registry editor on the Pocket PC or use the Windows CE Remote Registry Editor and change the name of the DLL listed under the DLL value for your item. You then open the Today screen Control Panel and enable or disable another item and close the Control Panel. This series of actions causes the Today screen to free all DLLs and reload the ones listed in the registry. Because you have just changed the DLL value to some filename that doesn’t exist, the Today screen can’t load that DLL, thereby allowing Microsoft eMbedded Visual C++ to download a new copy.

The PowerBar Custom Today Screen Item

The PowerBar example is a Today screen extension that displays the status of the battery as a bar running across the item window. PowerBar includes an options dialog that conforms to the look and feel of the options dialogs of the other Today screen items. Using the options dialog, you can change the height of the PowerBar item from a wide bar that displays an icon and a text display of the battery state to a thin 5-pixel bar that takes up very little room on your Today screen. Tapping the PowerBar item launches the Power Control Panel applet.

To install PowerBar, you need to edit the Pocket PC registry to add an entry for PowerBar under [HKEY_LOCAL_MACHINE]\Software\Microsoft\Today\Items, as I explained earlier. For the DLL name, use \Windows\Powerbar.dll. Figure 15–2 shows the Today screen with the PowerBar custom item. Figure 15-3 shows the PowerBar source code.

Click to view graphic
Click to view graphic

Figure 15-2 The Today screen with the PowerBar custom item displayed

This example has an additional source code file, PowerBar.def. Def files provide a method for defining specific ordinal values for exported functions. In the case of Today screen items, the exported function InitializeCustomItem and the options dialog box procedure must be assigned ordinals 240 and 241, respectively.

Figure 15-3 The PowerBar example

PowerBar.def

EXPORTS
 
InitializeCustomItem @ 240 NONAME
CustomItemOptionsDlgProc @ 241 NONAME

PowerBar.rc

//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//======================================================================
#include "windows.h"
#include "aygshell.h"
#include "todaycmn.h"
#include "PowerBar.h"
 
ID_ICON ICON "PowerBar.ico"
//----------------------------------------------------------------------
// Options dialog box template
//
IDD_TODAY_CUSTOM    DIALOG DISCARDABLE    0, 15, 134, 145
STYLE DS_CONTROL | WS_POPUP | WS_VISIBLE 
CAPTION "Settings"
BEGIN
    LTEXT    "Today : PowerBar Options", 
                       IDC_STATIC_TITLE,    4,   3, 124,  10
 
    ICON  ID_ICON,                    -1,   3,  20,  10,  10
    LTEXT "PowerBar - Written for the book Programming Windows \
           CE Copyright 2001 Douglas Boling"
                                      -1,  30,  20, 102,  30
 
    LTEXT "Bar Height",               -1,   3,  60, 124,  10
    AUTORADIOBUTTON "Short",    ID_SHORT,   7,  72, 124,  10
    AUTORADIOBUTTON "Medium",     ID_MED,   7,  84, 124,  10
    AUTORADIOBUTTON "Tall",      ID_TALL,   7,  96, 124,  10
END

PowerBar.h

//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//
//================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0])) 
 
//----------------------------------------------------------------------

// Generic defines and data types
//
struct decodeUINT {                            // Structure associates
    UINT Code;                                 // messages 
                                               // with a function. 
    LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
}; 
struct decodeCMD {                             // Structure associates
    UINT Code;                                 // menu IDs with a 
    LRESULT (*Fxn)(HWND, WORD, HWND, WORD);    // function.
};
 
// Helper macro
#define  MyIsBtnChecked(a,b) \
    ((SendDlgItemMessage (a, b, BM_GETSTATE,0,0)&3)==BST_CHECKED)
 
//----------------------------------------------------------------------
// Generic defines used by application
#define  ID_ICON           100                 // Icon ID
 
#define  IDC_STATIC_TITLE  101
#define  IDC_STATIC_DESC   102
#define  ID_SHORT          103 
#define  ID_MED            104 
#define  ID_TALL           105
//----------------------------------------------------------------------
// Function prototypes
//
int MyRegisterClass (HINSTANCE hInst);
int MyGetSetTodayItemReg (int i, BOOL fRead);
 
// Window procedures
LRESULT TodayWndProc (HWND, UINT, WPARAM, LPARAM);
 
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoLButtonUpMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoClearCacheMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoQueryRefreshCacheMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);

PowerBar.c

//====================================================================
// PowerBar - An example Today screen item
//
// Written for the book Programming Windows CE

// Copyright (C) 2001 Douglas Boling
//====================================================================
#include <windows.h>                 // For all that Windows stuff
#include <aygshell.h>                // Pocket PC includes
#include <todaycmn.h>                // Today screen includes
#include "PowerBar.h"                // PowerBar includes
 
// Returns number of elements
#define TODAYWND  TEXT ("MyPowerBarWnd")
 
// Procedure defs
//
// Global data
//
HINSTANCE hInst;
int nBattValue = 0;
BOOL fAC = FALSE;
BOOL fCharging = FALSE;
BOOL fNewData = TRUE;
int nFontHeight;
 
// Message dispatch table for TodayWindowProc
const struct decodeUINT MainMessages[] = {
    WM_CREATE, DoCreateMain,
    WM_PAINT, DoPaintMain,
    WM_LBUTTONUP, DoLButtonUpMain,
    WM_TODAYCUSTOM_CLEARCACHE, DoClearCacheMain,
    WM_TODAYCUSTOM_QUERYREFRESHCACHE, DoQueryRefreshCacheMain,
};
 
//====================================================================
// DllMain - DLL initialization entry point
//
BOOL WINAPI DllMain (HANDLE hinstDLL, DWORD dwReason,
                     LPVOID lpvReserved) {
    switch (dwReason) {
    case DLL_PROCESS_ATTACH:
        hInst = (HINSTANCE) hinstDLL;
        break;
 
    case DLL_PROCESS_DETACH:
        // We do this so we can reload the DLL later.
        UnregisterClass (TODAYWND, hInst);
        break;
    }
    return TRUE;
}

//----------------------------------------------------------------------
// MyRegisterClass - Registers the item’s window class
//
int MyRegisterClass (HINSTANCE hInst) {
    WNDCLASS wc;
 
    // Register the item’s window class.
    memset (&wc, 0, sizeof (wc));
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = TodayWndProc;
    wc.hInstance = hInst;
    wc.lpszClassName = TODAYWND;
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
    return RegisterClass (&wc);
}
//======================================================================
// InitializeCustomItem - Entry point called by Today screen to 
// indicate the item window to be created
//
HWND APIENTRY InitializeCustomItem(TODAYLISTITEM *ptli, HWND hwndParent) {
 
    HWND hWnd;
    // See if not enabled.
    if (!ptli->fEnabled)
        return FALSE;
    MyRegisterClass (hInst);
 
    // Create a child window for our Today window entry.
    hWnd = CreateWindow (TODAYWND, NULL, WS_VISIBLE | WS_CHILD, 
                         0, 0, GetSystemMetrics (SM_CYSCREEN), 0,
                         hwndParent, NULL, hInst, 0);
    return hWnd;
}
//======================================================================
// Message handling procedures 
//
//======================================================================
// TodayWndProc - Window procedure for the Today entry child window
//
LRESULT TodayWndProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) {
    INT i;
    //
    // Search message list to see if we need to handle this
    // message. If in list, call procedure.
    //
    for (i = 0; i < dim(MainMessages); i++) {
        if (wMsg == MainMessages[i].Code)
            return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
    }

    return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                      LPARAM lParam) {
    HDC hdc;
    TEXTMETRIC tm;
 
    // Query height of default font.
    hdc = GetDC (hWnd);
    GetTextMetrics (hdc, &tm);
    nFontHeight = tm.tmHeight + tm.tmExternalLeading;
    ReleaseDC (hWnd, hdc);
 
    nBattValue = -1;  // Initialize the old battery value.
    return 0;
}
//----------------------------------------------------------------------
// DoQueryRefreshCacheMain - Process WM_TODAYCUSTOM_QUERYREFRESHCACHE 
// message for window.
//
LRESULT DoQueryRefreshCacheMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                                 LPARAM lParam) {
 
    TODAYLISTITEM *ptli = (TODAYLISTITEM *)wParam;
    SYSTEM_POWER_STATUS_EX sps;
 
    // Set the height of our entry.
    if ((ptli->grfFlags < 5) || (ptli->grfFlags > 23))
        ptli->cyp = 20;
    else
        ptli->cyp = ptli->grfFlags;
 
    // Check the power status.
    GetSystemPowerStatusEx (&sps, FALSE);
 
    // Save AC status.
    if (sps.ACLineStatus == 1)
        fAC = TRUE;
    else
        fAC = FALSE;
 
    // Save charging status.
    if (sps.BatteryFlag & 0x08)
        fCharging = TRUE;
    else
        fCharging = FALSE;
 

    // If the battery value has changed since the last check, 
    // set the flag to force a redraw of the Today screen.
    if (sps.BatteryLifePercent != nBattValue) {
        nBattValue = sps.BatteryLifePercent;
        fNewData = TRUE;
    } else
        fNewData = FALSE;
    return fNewData;
}
//----------------------------------------------------------------------
// DoClearCacheMain - Process WM_TODAYCUSTOM_CLEARCACHE message 
// for window.
//
LRESULT DoClearCacheMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                          LPARAM lParam) {
    // Nothing to do here since the example doesn’t cache data
    return 0;
}
//----------------------------------------------------------------------
// DoLButtonUpMain - Process WM_LBUTTONUP message for window.
//
LRESULT DoLButtonUpMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                         LPARAM lParam) {
    SHELLEXECUTEINFO se;
    DWORD dwAttr;
 
    // Launch the Control Panel’s power applet.
    memset (&se, 0, sizeof (se));
    se.cbSize = sizeof (se);
    se.hwnd = hWnd;
    se.lpFile = TEXT ("ctlpnl.exe");
    se.lpVerb = TEXT("open");
    se.lpDirectory = TEXT ("\\windows");
    se.lpParameters = TEXT ("powerg.cpl");
 
    // See if power cpl is a standalone exe.
    dwAttr = GetFileAttributes (TEXT("\\windows\\powerg.exe"));
    if (dwAttr != (DWORD)-1)
        se.lpFile = TEXT ("powerg.exe");
 
    ShellExecuteEx (&se);    // Launch the Control Panel.
    return 0;
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                     LPARAM lParam) {

    PAINTSTRUCT ps;
    RECT rect;
    HDC hdc;
    TCHAR szText[32];
    int nPercent;
    COLORREF rgbLeft = RGB (0, 255, 0);
    HICON hIcon;
    HBRUSH hbr;
 
    // Ensure a valid battery value.
    nPercent = nBattValue;
    if (nBattValue == 255) {
        nPercent = 100;
        if (!fCharging && !fAC) 
            rgbLeft = RGB (255, 0, 0);
    } else if (nBattValue < 33) {
        rgbLeft = RGB (255, 0, 0);
    } 
 
    hdc = BeginPaint (hWnd, &ps);
    GetClientRect (hWnd, &rect);
 
    // Draw icon if room.
    if (rect.bottom - rect.top > 18) {
        hIcon = LoadImage (hInst, MAKEINTRESOURCE (ID_ICON), 
                           IMAGE_ICON, 16, 16, 0);
        DrawIcon (hdc, 2, 2, hIcon);
        DeleteObject (hIcon);
    }
    // Draw percent bar.
    hbr = CreateSolidBrush (rgbLeft);
    rect.left += 30;
    rect.right -= 5;
    rect.right = rect.left + ((rect.right - rect.left)*nPercent)/100;
    rect.top++;
    rect.bottom--;
    FillRect (hdc, &rect, hbr);
    DeleteObject (hbr);
 
    // Draw text percent if room.
    // Ask for rect again since we messed it up above.
    GetClientRect (hWnd, &rect);
    if (rect.bottom - rect.top > nFontHeight) {
        if (fCharging)
            lstrcpy (szText, TEXT ("Charging"));
        else if (!fAC && (nBattValue > 100))
            lstrcpy (szText, TEXT ("Unknown"));

        else
            wsprintf (szText, TEXT ("%02d%%"), nPercent);
        SetBkMode (hdc, TRANSPARENT);
        DrawText (hdc, szText, -1, &rect, DT_CENTER | DT_SINGLELINE | 
                  DT_VCENTER);
    }
    EndPaint (hWnd, &ps);
 
    // Reset my "redraw now" flag.
    fNewData = FALSE;
    return 0;
}
//======================================================================
// CustomItemOptionsDlgProc - Options Dialog box procedure 
//
BOOL CALLBACK CustomItemOptionsDlgProc (HWND hWnd, UINT wMsg, 
                                        WPARAM wParam, LPARAM lParam) {
    static TODAYLISTITEM *ptli;
    static HFONT hFont;
    WORD wID;
    int i;
 
    switch (wMsg) {
    case WM_INITDIALOG:
        {
            TEXTMETRIC tm;
            LOGFONT lf;
            HDC hdc;
            SHINITDLGINFO shidi;
 
            // Create a Done button and size dialog.
            shidi.dwMask = SHIDIM_FLAGS;
            shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLG;
            shidi.hDlg = hWnd;
            SHInitDialog(&shidi);
 
            ptli = (TODAYLISTITEM *)lParam;
 
            // Jump through hoops to look like 
            // other Today Options dialogs.
            hdc = GetDC (hWnd);
            GetTextMetrics (hdc, &tm);
            memset (&lf, 0, sizeof (lf));
            // Create proper font. It’s not 8 or 9 pt; it must be 8.5.
            lf.lfHeight = -1 * 
                          (17 * GetDeviceCaps (hdc, LOGPIXELSY)/72)/2;
            lf.lfWeight = FW_SEMIBOLD;

            lf.lfPitchAndFamily = tm.tmPitchAndFamily;
            lstrcpy (lf.lfFaceName, TEXT("Tahoma"));
            hFont = CreateFontIndirect (&lf);
            ReleaseDC (hWnd, hdc);
            // Query bar size setting from registry.
            i = MyGetSetTodayItemReg (0, TRUE);
            if (i == 0) i = 23;
            if (i < 16)
                wID = ID_SHORT;
            else if (i < 20)
                wID = ID_MED;
            else
                wID = ID_TALL;
            CheckRadioButton (hWnd, ID_SHORT, ID_TALL, wID);
        }
        break;
 
    case WM_DESTROY:
        if (hFont) 
            DeleteObject (hFont);
        break;
        
    case WM_PAINT:
        {
            // Draw a line 24 pixels down from the top per spec.
            PAINTSTRUCT ps;
            HDC hdc;
            RECT rect;
            HPEN hOld, hPen = GetStockObject (BLACK_PEN);
 
            GetClientRect (hWnd, &rect);
            hdc = BeginPaint (hWnd, &ps);
            rect.top = rect.top + 23;
            rect.bottom = rect.top;
            hOld = (HPEN)SelectObject (hdc, hPen);
            Polyline (hdc, (LPPOINT)&rect, 2);
 
            // Draw this line to separate about data from radio buttons.
            rect.top += 70;
            rect.bottom += 70;
            Polyline (hdc, (LPPOINT)&rect, 2);
            SelectObject (hdc, hOld);
            EndPaint (hWnd, &ps);
        }
        break;

    case WM_CTLCOLORSTATIC:
        // Modify the color and font of the header text string.
        if ((HWND)lParam != GetDlgItem (hWnd, IDC_STATIC_TITLE)) 
            break;
        SelectObject ((HDC)wParam, hFont);
        SetTextColor ((HDC)wParam, RGB (0, 0, 156));
        SetBkColor ((HDC)wParam, RGB (255, 255, 255));
        return (BOOL)GetStockObject (WHITE_BRUSH);
        
    case WM_COMMAND:
        wID = LOWORD (wParam);
        switch (wID) {
        case IDOK:
            i = 20;
            if (MyIsBtnChecked (hWnd, ID_MED))
                i = 16;
            else if (MyIsBtnChecked (hWnd, ID_SHORT))
                i = 5;
            // Save the height value.
            MyGetSetTodayItemReg (i, FALSE);
            ptli->grfFlags = i;
        case IDCANCEL:
            EndDialog (hWnd, 0);
            break;
        }
        break;
    }
    return FALSE;
}
//----------------------------------------------------------------------
// MyGetSetTodayItemReg - Writes the Flags value of the Today item’s 
// registry entry
//      
int MyGetSetTodayItemReg (int nFlagData, BOOL fRead) {
    HKEY hKey, hSubKey = 0;
    int rc, i = 0;
    DWORD dwType, dwSize;
    TCHAR szKey[128];
    TCHAR szDll[MAX_PATH];
    TCHAR szName[MAX_PATH];
 
    GetModuleFileName (hInst, szName, dim (szName));
 
    // Open the Today screen’s item key.
    rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, 
                       TEXT ("Software\\Microsoft\\today\\items"),
                       0, 0, &hKey);

    // Enumerate the item list until
    // we find a key with our DLL name.
    while (rc == ERROR_SUCCESS) {
        dwSize = sizeof (szKey);
        rc = RegEnumKeyEx (hKey, i++, szKey, &dwSize, NULL, NULL, NULL, NULL);
        if (rc != ERROR_SUCCESS)
            break;
        // Open the subkey.
        rc = RegOpenKeyEx (hKey, szKey, 0, 0, &hSubKey);
        if (rc == ERROR_SUCCESS) {
            // Get DLL name.
            dwSize = sizeof (szDll);
            rc = RegQueryValueEx (hSubKey, TEXT ("DLL"), 0, &dwType, 
                                  (PBYTE)szDll, &dwSize);
            if (rc == ERROR_SUCCESS) {
                // See if this is us.
                if (lstrcmpi (szDll, szName) == 0) 
                    break;  //Yes!
            }
            RegCloseKey (hSubKey);
            hSubKey = 0;
        }
    }
    if (hSubKey) {
        if (fRead) {
            dwSize = sizeof (DWORD);
            RegQueryValueEx (hSubKey, TEXT("Flags"), 0, &dwType, 
                             (PBYTE)&rc, &dwSize);
        }else
            RegSetValueEx (hSubKey, TEXT("Flags"), 0, REG_DWORD, 
                           (PBYTE)&nFlagData, sizeof (DWORD));
        RegCloseKey (hSubKey);
    }
    RegCloseKey (hKey);
    return rc;
}

The code that displays the Today screen item is not complex. In the InitializeCustomItem call, PowerBar registers the window class and creates the child window. In the window procedure, the code that handles the WM_TODAYCUSTOM_QUERYREFRESHCACHE message sets the cyp field of the TODAYLISTITEM structure to the proper height, which is configurable through the options dialog. The routine then checks the power status of the system by calling GetSystemPowerStatusEx. If the battery level has changed since the last check, the routine returns TRUE, forcing the Today screen to redraw the item. In the WM_PAINT handler, the bar is drawn across the window using the rectangle function. Depending on the height of the window, the icon is drawn and the power level is printed in the window.

The options dialog procedure, CustomItemOptionsDlgProc, goes to great lengths to provide the proper look to the dialog box. To this end, a custom font, 8.5-point Tahoma, is used to display the top line of text in the dialog box. In addition, this line of text is displayed in blue and a solid line is drawn 23 pixels below the top of the dialog. These customizations match the look of the Today items dialog.

The font is created in the WM_INITDIALOG message. To override the drawing of the top line of text, the dialog procedure fields the WM_CTLCOLORSTATIC message. The following code shows how—after checking which control is being drawn—the dialog box procedure sets the text color and the font so that the text is displayed with the customized look.

case WM_CTLCOLORSTATIC:
    // Modify the color and font of the header text string.
    if ((HWND)lParam != GetDlgItem (hWnd, IDC_STATIC_TITLE)) 
        break;
    SelectObject ((HDC)wParam, hFont);
    SetTextColor ((HDC)wParam, RGB (0, 0, 156));
    SetBkColor ((HDC)wParam, RGB (255, 255, 255));
    return (BOOL)GetStockObject (WHITE_BRUSH);

The Today screen is an example of the extensibility of the Pocket PC shell. Applications that provide an additional Today screen item to summarize their data provide that extra bit of integration that users appreciate.


Previous   |  Table of Contents   |   Next




Top of Page


Last Updated: Saturday, July 7, 2001