Programming Microsoft Windows Ce Net 3Rd [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Microsoft Windows Ce Net 3Rd [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Custom Input Methods

The soft input panel, or SIP, provides Pocket PC users with a method of "keyboard"-style input. I put keyboard in quotes because although the application sees keyboard messages from the SIP, the user might be entering those characters using a handwriting recognizer. The Pocket PC comes bundled with a few ways of entering character data. The user can use either a tiny drawing of a keyboard on which the user can tap in characters or some type of handwriting recognizer that interprets strokes that the user makes with a stylus. You can also design your own method of input rather easily. A component that provides this functionality is called an input method (IM), and it's merely a COM object that exports an IInputMethod interface, and optionally an IInputMethod2 interface.

The IInputMethod2 interface adds new methods for dealing with the Input Method Editor (IME). The IME is used to propose a series of candidate characters in response to input in the SIP. The SIP doesn't provide enough room to allow the user to enter thousands of discrete characters, so the IME is used when working with Asian languages. Unless your SIP needs to interface with the IME, the IInputMethod interface should be sufficient since it is compatible with all versions of Windows CE that support a SIP. No matter which interface is exposed, the purpose of the COM object is to create an input method window in response to requests from the input panel.


The Components of a SIP


A SIP is composed of two main components—the input panel and the input method. The input panel is supplied by the system. It creates the input panel window and provides both the message loop processing for the SIP and the window procedure for the input panel window. The input panel cooperates with the taskbar or another shell program to provide the user with the ability to switch between a number of installed input methods.

The input method is the installable portion of the SIP. It's responsible for translating pen strokes and taps into keyboard input. The input method is also responsible for the look and feel of the SIP. In almost all cases, the input method creates a window that is a child of the input panel window. Within that child window, the input method draws its interface and interprets mouse messages. The input method then calls back to the input panel when it wants to generate a key event.

Each of these two components implements a COM interface, which then becomes the interface between them. The input method implements one of the IInputMethodxx interfaces, while the input panel implements three very similar interfaces: IIMCallback, IIMCallbackEx, and IIMCallback2. In the following paragraphs, I'll talk about the IInputMethod, IInputMethod2, IIMCallback, and IIMCallback2 interfaces.

The interaction between the input panel and the input method is driven by the input panel. For the most part, the input method simply responds to calls made to its IInputMethod methods. Calls are made when the input method is loaded, when it's unloaded, and when it's shown or hidden. In response, the input method must draw in its child window, interpret the user's actions, and call methods in the IIMCallback interface to send keys to the system or to control the input panel's window.

Input methods are implemented as COM in-proc servers. Because of this, they must conform to the standard COM in-proc server specifications. This means that an input method is implemented as a DLL that exports DllGetClassObject and DllCanUnloadNow functions. Input methods must also export DllRegisterServer and DllUnregisterServer functions that perform the necessary registry registration and deregistration for the server DLL.


Threading Issues with Input Methods


Because the input panel and input method components are so tightly interrelated, you must follow a few rules when writing an input method. While you can use multiple threads in an input method, the interaction between the input panel and the input method is strictly limited to the input panel's primary thread. This means that the input method should create any windows during calls to methods in the IInputMethod interface. This ensures that these windows will use the same message loop as the input panel's window. This, in turn, allows the input panel to directly call the input method's window procedures, as necessary. In addition, that same thread should make all calls made back to the IIMCallback interface.

In short, try not to multithread your input method. If you must use multiple threads, create all windows in your input method using the input panel's thread. Secondary threads can be created, but they can't call the IIMCallback interface and they shouldn't create any windows.


The IInputMethod and IInputMethod2 Interfaces


The IInputMethod interface is the core of an IM. Using the interface's methods, an IM should create any windows, react to any changes in the parent input panel window, and provide any cleanup when it's released. The IInputMethod interface exports the following methods in addition to the standard IUnknown methods:



IInputMethod::SelectThe user has selected the IM. The IM should create its window.



IInputMethod::Deselect The user has selected another IM. The IM should destroy its window.



IInputMethod::ShowingThe IM window is about to be shown.



IInputMethod::HidingThe IM window is about to be hidden.



IInputMethod::GetInfoThe system is querying the IM for information.



IInputMethod::ReceiveSipInfoThe system is providing information to the IM.



IInputMethod::RegisterCallbackThe system is providing a pointer to the IIMCallback interface.



IInputMethod::GetImDataThe IM is queried for IM-specific data.



IInputMethod::SetImDataThe IM is provided IM-specific data.



IInputMethod::UserOptionsDlgThe IM should display an options dialog box to support the SIP Control Panel applet.



In addition to the preceding methods, the IInputMethod2 interface has the following methods:



IInputMethod2::RegisterCallback2The system is sending a pointer to the IIMCallback2 interface.



IInputMethod2::SetIMMActiveContextThe system is informing the IM of the current state of the IME.



Let's now look at these methods in detail so that we can understand the processing necessary for each. The descriptions of the methods for the IInputMethod interface also apply for the similarly named methods in the IInputMethod2 interface.

IInputMethod::Select


When the user chooses your input method, the DLL that contains your IM is loaded and the Select method is called. This method is prototyped as

HRESULT IInputMethod::Select (HWND hwndSip);

The only parameter is the handle to the SIP window that's the parent of your input method's main window. You should return S_OK to indicate success or E_FAIL if you can't create and initialize your input method successfully.

When the Select method is called, the IM will have just been loaded into memory and you'll need to perform any necessary initialization. This includes registering any window classes and creating the input method window. The IM should be created as a child of the SIP window because the SIP window is what will be shown, hidden, and moved in response to user action. You can call GetClientRect with the parent window handle to query the necessary size of your input window.

IInputMethod::GetInfo


After the input panel has loaded your IM, it calls the GetInfo method. The input panel calls this method to query the bitmaps that represent the IM. These bitmaps appear in the SIP button on the taskbar. In addition, the IM can provide a set of flags and the size and location on the screen where it would like to be displayed. This method is prototyped as

HRESULT IInputMethod::GetInfo (IMINFO *pimi);

The only parameter is a pointer to an IMINFO structure that the IM must fill out to give information back to the SIP. The IMINFO structure is defined as

typedef struct  {
DWORD cbSize;
HANDLE hImageNarrow;
HANDLE hImageWide;
int iNarrow;
int iWide;
DWORD fdwFlags;
RECT rcSipRect;
} IMINFO;

The first Chapter 17. When the SIPF_DISABLECOMPLETION flag is set, the auto-completion function of the SIP is disabled.

Finally, the rcSipRect field should be filled with the default rectangle for the input method. Unless you have a specific size and location on the screen for your IM, you can simply query the client rectangle of the parent SIP window for this rectangle. Note that just because you request a size and location of the SIP window doesn't mean that the window will have that rectangle. You should always query the size of the parent SIP window when laying out your IM window.

IInputMethod::ReceiveSipInfo


The ReceiveSipInfo method is called by the input panel when the input panel is shown and then again when an application moves or changes the state of the input panel. The method is prototyped as

HRESULT IInputMethod::ReceiveSipInfo (SIPINFO *psi);

The only parameter is a pointer to Chapter 17. When this method is called, only two of the fields are valid—the fdwFlags field and the rcSipRect field. The rcSipRect field contains the size and location of the input panel window, while the fdwFlags field contains the SIPF_xxx flags previously described. In response to the ReceiveSipInfo method call, the IM should save the new state flags and rectangle.

IInputMethod::RegisterCallback


The input panel calls the RegisterCallback method once, after the input method has been selected. The method is prototyped as

HRESULT IInputMethod::RegisterCallback (IIMCallback *lpIMCallback);

This method is called to provide a pointer to the IIMCallback interface. The only action the IM must take is to save this pointer so that it can be used to provide feedback to the input panel.

IInputMethod::Showing and IInputMethod::Hiding


The input panel calls the Showing and Hiding methods just before the IM is shown or hidden. Both these methods have no parameters and you should simply return S_OK to indicate success. The Showing method is also called when the panel is moved or resized. This makes the Showing method a handy place for resizing the IM child window to properly fit in the parent input panel window.

IInputMethod::GetImData and IInputMethod::SetImData


The GetImData and SetImData methods give you a back door into the IM for applications that need to have a special communication path between the application and a custom IM. This arrangement allows a specially designed IM to provide additional data to and from applications. The two methods are prototyped as

HRESULT IInputMethod::GetImData (DWORD dwSize, void* pvImData);
HRESULT IInputMethod::SetImData (DWORD dwSize, void* pvImData);

For both functions, pvImData points to a block of memory in the application. The dwSize parameter contains the size of the memory block.

When an application is sending data to a custom IM, it calls SHSipInfo with the SPI_SETSIPINFO flag. The pointer to the buffer and the size of the buffer are specified in the pvImData and dwImDataSize fields of the SIPINFO structure. If these two fields are nonzero, the input panel then calls the SetImData method with the pointer and the size of the buffer contained in the two parameters of the method. The input method then accepts the data in the buffer pointed to by pvImData. When an application calls SHSipInfo with the SPI_GETSIPINFO structure and nonzero values in pvImData and dwImDataSize, the input panel then calls the GetImData method to retrieve data from the input method.

IInputMethod::Deselect


When the user or a program switches to a different default IM, the input panel calls Deselect. Your input method should save its state (its location on the screen, for example), destroy any windows it has created, and unregister any window classes it has registered. It should also destroy any image lists it's still maintaining. The prototype for this method is

HRESULT IInputMethod::Deselect (void);

After the Deselect method is called, the SIP will unload the input method DLL.

IInputMethod::UserOptionsDlg


The UserOptionsDlg method isn't called by the input panel. Instead, the input panel's Control Panel applet calls this method when the user taps the Options button. The IM should display a dialog box that allows the user to configure any settable parameters in the input method. The UserOptionsDlg method is prototyped as

HRESULT IInputMethod::UserOptionsDlg (HWND hwndParent);

The only parameter is the handle to the window that should be the parent window of the dialog box. Because the IM might be unloaded after the dialog box is dismissed, any configuration data should be saved in a persistent place such as the registry, where it can be recalled when the input panel is loaded again.

The following two methods are supported only in the IInputMethod2 interface. The IInputMethod2 interface is derived from IInputMethod; all the methods previously described are therefore implemented in IInputMethod2.

IInputMethod2::RegisterCallback2


The input panel calls the RegisterCallback2 method once, after the input method has been selected. The method is prototyped as

HRESULT IInputMethod2::RegisterCallback2 (IIMCallback2 *lpIMCallback);

This method is called to provide a pointer to the IIMCallback2 interface. The only action the IM must take is to save this pointer so that it can be used to provide feedback to the input panel.

IInputMethod2::SetIMMActiveContext


The input panel calls SetIMMActiveContext to inform the input method of changes in state of the IME. The method is prototyped as

HRESULT SetIMMActiveContext (HWND hwnd, BOOL bOpen, DWORD dwConversion, 
DWORD dwSentence, DWORD hkl);

The hwnd parameter is the handle of window control that has changed state. The bOpen parameter indicates whether the IME is on or off. The dwConversion and dwSentence parameters provide status on the current mode of the IME. The hkl parameter contains the handle to the current active keyboard layout.


The IIMCallback and IIMCallback2 Interfaces


The IIMCallback interface allows an IM to call back to the input panel for services such as sending keys to the operating system. Aside from the standard IUnknown methods that can be ignored by the IM, IIMCallback exposes only four methods:



IIMCallback::SetImInfoSets the bitmaps used by the input panel as well as the location and visibility state of the input method



IIMCallback::SendVirtualKeySends a virtual key to the system



IIMCallback::SendCharEventsSends Unicode characters to the window with the current focus



IIMCallback::SendStringSends a string of characters to the window with the current focus



It's appropriate that the IIMCallback interface devotes three of its four methods to sending keys and characters to the system because that's the primary purpose of the IM.

The IIMCallback2 interface adds one method:



IIMCallback2::SendAlternatives2Sends data from the input method to the IME



Let's take a quick look at each of these methods.

IIMCallback::SetImInfo


The SetImInfo method allows the IM control over its size and location on the screen. This method can also be used to set the bitmaps representing the IM. The method is prototyped as

HRESULT IIMCallback::SetImInfo (IMINFO *pimi);

The only parameter is a pointer to an IMINFO structure. This is the same structure that the IM uses when it calls the GetInfo method of the IInputMethod interface, but I'll repeat it here for clarity.

typedef struct  {
DWORD cbSize;
HANDLE hImageNarrow;
HANDLE hImageWide;
int iNarrow;
int iWide;
DWORD fdwFlags;
RECT rcSipRect;
} IMINFO;

This structure enables an IM to provide the input panel with the information that the IM retrieved in GetInfo. The IM must correctly fill in all the fields in the IMINFO structure because it has no other way to tell the input panel to look at only one or two of the fields. You shouldn't re-create the image lists when you're calling SetImInfo; instead, use the same handles you passed in GetInfo—unless you want to change the image lists used by the input panel. In that case, you'll need to destroy the old image lists after you've called SetImInfo.

You can use SetImInfo to undock the input panel and move it around the screen by clearing the SIPF_DOCKED flag in fdwFlags and specifying a new size and location for the panel in the rcSipRect field. Because Windows CE doesn't provide system support for dragging an input panel around the screen, the IM is responsible for providing such a method. The sample IM that I present beginning on page 907 supports dragging the input panel around by creating a gripper area on the side of the panel and interpreting the stylus messages in this area to allow the panel to be moved around the screen.

IIMCallback::SendVirtualKey


The SendVirtualKey method is used to send virtual key codes to the system. The difference between this method and the SendCharEvents and SendString methods is that this method can be used to send noncharacter key codes, such as those from cursor keys and shift keys, that have a global impact on the system. Also, key codes sent by SendVirtualKey are affected by the system key state. For example, if you send an a character and the Shift key is currently down, the resulting WM_CHAR message contains an A character. SendVirtualKey is prototyped as

HRESULT IIMCallback::SendVirtualKey (BYTE bVk, DWORD dwFlags);

The first parameter is the virtual key code of the key you want to send. The second parameter can contain one or more flags that help define the event. The flags can be either 0 or a combination of flags. You would use KEYEVENTF_KEYUP to indicate that the event is a key up event as opposed to a key down event and KEYEVENTF_SILENT, which specifies that the key event won't cause a key click to be played for the event. If you use SendVirtualKey to send a character key, the character will be modified by the current shift state of the system.

IIMCallback::SendCharEvents


The SendCharEvents method can be used to send specific characters to the window with the current focus. The difference between this method and the SendVirtualKey method is that SendCharEvents gives you much more control over the exact information provided in the WM_KEYxxx and WM_CHAR messages generated. Instead of simply sending a virtual key code and letting the system determine the proper character, this method allows you to specify the virtual key and associate a completely different character or series of characters generated by this event. For example, in a simple case, calling this method once causes the messages WM_KEYDOWN, WM_CHAR, and WM_KEYUP all to be sent to the focus window. In a more complex case, this method can send a WM_KEYDOWN and multiple WM_CHAR messages, followed by a WM_KEYUP message.

This method is prototyped as

HRESULT IIMCallback::SendCharEvents (UINT uVK, UINT uKeyFlags, 
UINT uChars, UINT *puShift, UINT *puChars);

The first parameter is the virtual key code that will be sent with the WM_KEYDOWN and WM_KEYUP messages. The second parameter is an unsigned integer containing the key flags that will be sent with the WM_KEYDOWN and WM_KEYUP messages. The third parameter is the number of WM_CHAR messages that will be generated by this one event. The next parameter, puShift, should point to an array of key state flags, while the final parameter, puChars, should point to an array of Unicode characters. Each entry in the shift array will be joined with the corresponding Unicode character in the character array when the WM_CHAR messages are generated. This allows you to give one key on the IM keyboard a unique virtual key code and to generate any number of WM_CHAR messages, each with its own shift state.

IIMCallback::SendString


You use the SendString method to send a series of characters to the focus window. The advantage of this function is that an IM can easily send an entire word or sentence, and the input panel will take care of the details such as key down and key up events. The method is prototyped as

HRESULT IIMCallback::SendString (LPTSTR ptszStr, DWORD dwSize);

The two parameters are the string of characters to be sent and the number of characters in the string.

IIMCallback2::SendAlternatives2


The SendAlternatives2 method provides a mechanism for the input method to send alternative characters to the IME. For languages with hundreds or thousands of characters, the input method might have to guess at the intended character entered by the user. These guesses or alternative characters are sent using SendAlternatives2 to the IME so that it can present the alternatives to the active control. If the control doesn't handle the alternative suggestions, the first character in the list is used as the correct character. The prototype of SendAlternatives2 is

HRESULT SendAlternatives2(LMDATA * plmd);

The one parameter is a pointer to an LMDATA structure defined as

typedef struct  _tagLMDATA {
DWORD dwVersion;
DWORD flags;
DWORD cnt;
DWORD dwOffsetSymbols;
DWORD dwOffsetSkip;
DWORD dwOffsetScore;
}LMDATA;

The version field should be set to 0x10000. The flags field describes the format of the data in the table provided. The cnt field contains the number of entries in the table. The dwOffsetSymbols, dwOffsetSkip, and dwOffsetScore fields contain the offset of the start of the respective tables containing the alternative data. The data in the tables vary depending on how the IME and the input method agree to share data.


The NumPanel Example Input Method


The NumPanel example code demonstrates a simple IM. NumPanel gives a user a simple numeric keyboard including keys 0 through 9 as well as the four arithmetic operators: +, -, *, and / and the equal sign key (=). Although it's not particularly useful to the user, NumPanel does demonstrate all the requirements of an input method. The NumPanel example is different from the standard IMs that come with the Pocket PC in that it can be undocked. The NumPanel IM has a gripper bar on the left side of the window that can be used to drag the SIP around the screen. When a user double-taps the gripper bar, the SIP snaps back to its docked position. Figure 18-3 shows the NumPanel IM in its docked position, while Figure 18-4 shows the same panel undocked.


Figure 18-3: The NumPanel IM window in its docked position


Figure 18-4: The NumPanel IM window undocked

The source code that implements NumPanel is divided into two main files, IInputMethod interface and the IClassFactory interface. DllMain and the other functions necessary to implement a COM in-proc server. Listing 18-2.

Listing 18-2: The NumPanel source code







NumPanel.def
;
;Standard COM library DEF file
;
LIBRARY NUMPANEL.DLL
EXPORTS
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE


IMCommon.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h" // For all that Windows stuff
#include "NumPanel.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "NumPanel.ico" // Module icon
NarrowBmp BITMAP "nkbd.bmp" // Bmp used in image list
NarrowMask BITMAP "nmask.bmp" // Mask used in image list
WideBmp BITMAP "widekbd.bmp" // Bmp used in image list
WideMask BITMAP "widemask.bmp" // Mask used in image list


IMCommon.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#define ID_ICON 1
// **** Start of Generic COM declarations ****
//======================================================================
// MyClassFactory - Object declaration
//
class MyClassFactory : public IClassFactory {
private:
long m_lRef;
public:
MyClassFactory();
~MyClassFactory();
//IUnknown methods
STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppv);
STDMETHODIMP_(ULONG) AddRef (THIS);
STDMETHODIMP_(ULONG) Release (THIS);
//IClassFactory methods
STDMETHODIMP CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid,
LPVOID *ppv);
STDMETHODIMP LockServer (BOOL fLock);
};
// **** End of Generic OLE declarations ****
//======================================================================
// MyIInputMethod - Object declaration
//
class MyIInputMethod : public IInputMethod2 {
private:
long m_lRef;
HWND m_hwndParent;
HWND m_hwndMyWnd;
HIMAGELIST m_himlWide;
HIMAGELIST m_himlNarrow;
IIMCallback *m_pIMCallback;
public:
MyIInputMethod();
~MyIInputMethod();
//IUnknown methods
STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppvObj);
STDMETHODIMP_(ULONG) AddRef (THIS);
STDMETHODIMP_(ULONG) Release (THIS);
//IInputMethod
HRESULT STDMETHODCALLTYPE Select (HWND hwndSip);
HRESULT STDMETHODCALLTYPE Deselect (void);
HRESULT STDMETHODCALLTYPE Showing (void);
HRESULT STDMETHODCALLTYPE Hiding (void);
HRESULT STDMETHODCALLTYPE GetInfo (IMINFO __RPC_FAR *pimi);
HRESULT STDMETHODCALLTYPE ReceiveSipInfo (SIPINFO __RPC_FAR *psi);
HRESULT STDMETHODCALLTYPE RegisterCallback (
IIMCallback __RPC_FAR *lpIMCallback);
HRESULT STDMETHODCALLTYPE GetImData (DWORD dwSize, LPVOID pvImData);
HRESULT STDMETHODCALLTYPE SetImData (DWORD dwSize, LPVOID pvImData);
HRESULT STDMETHODCALLTYPE UserOptionsDlg (HWND hwndParent);
//IInputMethod2
HRESULT STDMETHODCALLTYPE SetIMMActiveContext(HWND hwnd, BOOL bOpen,
DWORD dwConversion, DWORD dwSentence, DWORD hkl);
HRESULT STDMETHODCALLTYPE RegisterCallback2 (
IIMCallback2 __RPC_FAR *lpIMCallback);
};


NumPanel.h
//======================================================================
// NPWnd.h - An include file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#ifdef __cplusplus
extern "C"{
#endif
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
#define MYSIPCLS TEXT ("MyNumPanelWndCls")
#define MYMSG_METHCALL (WM_USER+100) // Used to pass info
#define MSGCODE_REGCALLBACK 0 // Notification codes for
#define MSGCODE_GETINFO 1 // MYMSG_METHCALL
#define MSGCODE_SETINFO 2
#define MSGCODE_REGCALLBACK2 3

#define GRIPWIDTH 9 // Width of the gripper
#define FLOATWIDTH 200 // Width of floating wnd
#define FLOATHEIGHT 100 // Height of floating wnd
#define CXBTNS 5 // Num columns of buttons
#define CYBTNS 3 // Num rows of buttons
//
// Local data structure for keypad IM window
//
typedef struct {
DWORD dwBtnDnFlags;
IIMCallback *pIMCallback;
IIMCallback2 *pIMCallback2;
RECT rectDocked;
BOOL fMoving;
POINT ptMovBasis;
POINT ptMovStart;
IMINFO imi;
RECT rectLast;
} SIPWNDSTRUCT, *LPSIPWNDSTRUCT;
INT DrawButton (HDC hdc, RECT *prect, LPTSTR pChar, BOOL fPressed);
//
// Prototypes for functions implemented by custom IM code
//
HWND CreateIMWindow (HWND hwndParent);
int DestroyIMWindow (HWND hwnd);
LRESULT CALLBACK NPWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoCreateSip (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoSetSipInfo (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoPaintSip (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoMouseSip (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DoDestroySip (HWND, UINT, WPARAM, LPARAM);
#ifdef __cplusplus
}
#endif


IMCommon.cpp
//======================================================================
// IMCommon - Common code for a Windows CE input method
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#define INITGUID
#include <initguid.h>
#include <coguid.h>
#include <sip.h> // SIP includes
#include "IMCommon.h" // My IM common includes
#include "NumPanel.h" // IM window specific includes
long g_DllCnt = 0; // Global DLL reference count
extern "C" {
HINSTANCE hInst; // DLL instance handle
}
//
// GUID defines for my input method. Create a new one with GUIDGEN.
//
const GUID CLSID_NumPanel2 =
{ 0xc8311f61, 0x12df,0x4107,{0xb5,0xea,0xb0,0xb0,0xd5,0x5c,0xec,0x50}};
const TCHAR szCLSIDNumPanel2[] =
TEXT ("{C8311F61-12DF-4107-B5EA-B0B0D55CEC50}");
const TCHAR szFriendlyName[] = TEXT ("Numeric Keypad");
//======================================================================
// DllMain - DLL initialization entry point
//
BOOL WINAPI DllMain (HANDLE hinstDLL, DWORD dwReason,
LPVOID lpvReserved) {
hInst = (HINSTANCE)hinstDLL;
return TRUE;
}
//======================================================================
// DllGetClassObject - Exported function called to get pointer to
// Class factory object.
//
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv) {
MyClassFactory *pcf;
HRESULT hr;
// See if caller wants us.
if (IsEqualCLSID (rclsid, CLSID_NumPanel2)) {
// Create IClassFactory object.
pcf = new MyClassFactory();
if (pcf == NULL)
return E_OUTOFMEMORY;
// Call class factory's query interface method.
hr = pcf->QueryInterface (riid, ppv);
// This will cause an obj delete unless interface found.
pcf->Release();
return hr;
}
return CLASS_E_CLASSNOTAVAILABLE;
}
//======================================================================
// DllCanUnloadNow - Exported function called when DLL can unload
//
STDAPI DllCanUnloadNow () {
if (g_DllCnt)
return S_FALSE;
return S_OK;
}
//======================================================================
// DllRegisterServer - Exported function called to register the server
//
STDAPI DllRegisterServer () {
TCHAR szName[MAX_PATH+2];
TCHAR szTmp[128];
DWORD dwDisp;
HKEY hKey, hSubKey;
INT rc;
GetModuleFileName (hInst, szName, sizeof (szName));
// Open the key.
wsprintf (szTmp, TEXT ("CLSID\\%s"), szCLSIDNumPanel2);
rc = RegCreateKeyEx (HKEY_CLASSES_ROOT, szTmp, 0, TEXT("),
0, 0, NULL, &hKey, &dwDisp);
if (rc != ERROR_SUCCESS)
return E_FAIL;
// Set the friendly name of the SIP.
RegSetValueEx (hKey, TEXT ("), 0, REG_SZ, (PBYTE)szFriendlyName,
(lstrlen (szFriendlyName)+1) * sizeof (TCHAR));
// Create subkeys.
// Set the module name of the SIP.
rc = RegCreateKeyEx (hKey, TEXT ("InProcServer32"), 0, TEXT("),
0, 0, NULL, &hSubKey, &dwDisp);
rc = RegSetValueEx (hSubKey, TEXT ("), 0, REG_SZ, (PBYTE)szName,
(lstrlen (szName)+1) * sizeof (TCHAR));
RegCloseKey (hSubKey);
// Set the default icon of the server.
RegCreateKeyEx (hKey, TEXT ("DefaultIcon"), 0, TEXT("),
0, 0, NULL, &hSubKey, &dwDisp);
lstrcat (szName, TEXT (",0"));
RegSetValueEx (hSubKey, TEXT ("), 0, REG_SZ, (PBYTE)szName,
(lstrlen (szName)+1) * sizeof (TCHAR));
RegCloseKey (hSubKey);
// Set the flag indicating this is a SIP.
RegCreateKeyEx (hKey, TEXT ("IsSIPInputMethod"), 0, TEXT("),
0, 0, NULL, &hSubKey, &dwDisp);
lstrcpy (szTmp, TEXT ("1"));
RegSetValueEx (hSubKey, TEXT ("), 0, REG_SZ, (PBYTE)szTmp, 4);
RegCloseKey (hSubKey);
RegCloseKey (hKey);
return S_OK;
}
//======================================================================
// DllUnregisterServer - Exported function called to remove the server
// information from the registry
//
STDAPI DllUnregisterServer() {
INT rc;
TCHAR szTmp[128];
wsprintf (szTmp, TEXT ("CLSID\\%s"), szCLSIDNumPanel2);
rc = RegDeleteKey (HKEY_CLASSES_ROOT, szTmp);
if (rc == ERROR_SUCCESS)
return S_OK;
return E_FAIL;
}
//**********************************************************************
// MyClassFactory Object implementation
//----------------------------------------------------------------------
// Object constructor
MyClassFactory::MyClassFactory () {
m_lRef = 1; // Set ref count to 1 on create.
return;
}
//----------------------------------------------------------------------
// Object destructor
MyClassFactory::~MyClassFactory () {
return;
}
//----------------------------------------------------------------------
// QueryInterface - Called to see what interfaces this object supports
STDMETHODIMP MyClassFactory::QueryInterface (THIS_ REFIID riid,
LPVOID *ppv) {
// If caller wants our IUnknown or IClassFactory object,
// return a pointer to the object.
if (IsEqualIID (riid, IID_IUnknown) ||
IsEqualIID (riid, IID_IClassFactory)) {
*ppv = (LPVOID)this; // Return pointer to object.
AddRef(); // Increment ref to prevent delete on return.
return NOERROR;
}
*ppv = NULL;
return (E_NOINTERFACE);
}
//----------------------------------------------------------------------
// AddRef - Increment object ref count.
STDMETHODIMP_(ULONG) MyClassFactory::AddRef (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}
//----------------------------------------------------------------------
// Release - Decrement object ref count.
STDMETHODIMP_(ULONG) MyClassFactory::Release (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0)
delete this;
return cnt;
}
//----------------------------------------------------------------------
// LockServer - Called to tell the DLL not to unload even if use count is 0
STDMETHODIMP MyClassFactory::LockServer (BOOL fLock) {
if (fLock)
InterlockedIncrement (&g_DllCnt);
else
InterlockedDecrement (&g_DllCnt);
return NOERROR;
}
//----------------------------------------------------------------------
// CreateInstance - Called to have class factory object create other
// objects
STDMETHODIMP MyClassFactory::CreateInstance (LPUNKNOWN pUnkOuter,
REFIID riid,
LPVOID *ppv) {
MyIInputMethod *pMyIM;
HRESULT hr;
if (pUnkOuter)
return (CLASS_E_NOAGGREGATION);
if (IsEqualIID (riid, IID_IUnknown) ||
IsEqualIID (riid, IID_IInputMethod) ||
IsEqualIID (riid, IID_IInputMethod2)) {
// Create Input method object.
pMyIM = new MyIInputMethod();
if (!pMyIM)
return E_OUTOFMEMORY;
// See if object exports the proper interface.
hr = pMyIM->QueryInterface (riid, ppv);
// This will cause an obj delete unless interface found.
pMyIM->Release ();
return hr;
}
return E_NOINTERFACE;
}
//**********************************************************************
// MyIInputMethod Object implementation
//----------------------------------------------------------------------
// Object constructor
MyIInputMethod::MyIInputMethod () {
m_lRef = 1; // Set ref count to 1 on create.
g_DllCnt++;
return;
}
//----------------------------------------------------------------------
// Object destructor
MyIInputMethod::~MyIInputMethod () {
g_DllCnt--;
return;
}
//----------------------------------------------------------------------
// QueryInterface - Called to see what interfaces this object supports
STDMETHODIMP MyIInputMethod::QueryInterface (THIS_ REFIID riid,
LPVOID *ppv) {
// If caller wants our IUnknown or IID_IInputMethod2 object,
// return a pointer to the object.
if (IsEqualIID (riid, IID_IUnknown) ||
IsEqualIID (riid, IID_IInputMethod) ||
IsEqualIID (riid, IID_IInputMethod2)){
// Return ptr to object.
*ppv = (IInputMethod *)this;
AddRef(); // Increment ref to prevent delete on return.
return NOERROR;
}
*ppv = NULL;
return (E_NOINTERFACE);
}
//----------------------------------------------------------------------
// AddRef - Increment object ref count.
STDMETHODIMP_(ULONG) MyIInputMethod::AddRef (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}
//----------------------------------------------------------------------
// Release - Decrement object ref count.
STDMETHODIMP_(ULONG) MyIInputMethod::Release (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0) {
delete this;
return 0;
}
return cnt;
}
//----------------------------------------------------------------------
// Select - The IM has just been loaded into memory.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::Select (HWND hwndSip) {
HBITMAP hBmp, hbmpMask;
m_hwndParent = hwndSip;

// Create image list for narrow (16x16) image.
m_himlNarrow = ImageList_Create (16, 16, ILC_COLOR | ILC_MASK,
1, 0);
hBmp = LoadBitmap (hInst, TEXT ("NarrowBmp"));
hbmpMask = LoadBitmap (hInst, TEXT ("NarrowMask"));
ImageList_Add (m_himlNarrow, hBmp, hbmpMask);
DeleteObject (hBmp);
DeleteObject (hbmpMask);
// Create image list for wide (32x16) image.
m_himlWide = ImageList_Create (32, 16, ILC_COLOR | ILC_MASK, 1, 0);
hBmp = LoadBitmap (hInst, TEXT ("WideBmp"));
hbmpMask = LoadBitmap (hInst, TEXT ("WideMask"));
ImageList_Add (m_himlWide, hBmp, hbmpMask);
DeleteObject (hBmp);
DeleteObject (hbmpMask);
// Create SIP window.
m_hwndMyWnd = CreateIMWindow (hwndSip);
if (!IsWindow (m_hwndMyWnd))
return E_FAIL;
return S_OK;
}
//----------------------------------------------------------------------
// Deselect - The IM is about to be unloaded.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::Deselect (void) {
DestroyIMWindow (m_hwndMyWnd);
ImageList_Destroy (m_himlNarrow);
ImageList_Destroy (m_himlWide);
return S_OK;
}
//----------------------------------------------------------------------
// Showing - The IM is about to be made visible.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::Showing (void) {
return S_OK;
}
//----------------------------------------------------------------------
// Hiding - The IM is about to be hidden.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::Hiding (void) {
return S_OK;
}
//----------------------------------------------------------------------
// GetInfo - The SIP wants info from the IM.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::GetInfo (
IMINFO __RPC_FAR *pimi) {
pimi->cbSize = sizeof (IMINFO);
pimi->hImageNarrow = m_himlNarrow;
pimi->hImageWide = m_himlWide;
pimi->iNarrow = 0;
pimi->iWide = 0;
pimi->fdwFlags = SIPF_DOCKED;
pimi->rcSipRect.left = 0;
pimi->rcSipRect.top = 0;
pimi->rcSipRect.right = FLOATWIDTH;
pimi->rcSipRect.bottom = FLOATHEIGHT;
SendMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_GETINFO,
(LPARAM) pimi);
return S_OK;
}
//----------------------------------------------------------------------
// ReceiveSipInfo - The SIP is passing info to the IM.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::ReceiveSipInfo (
SIPINFO __RPC_FAR *psi) {
// Pass the sip info data to the window.
SendMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_SETINFO,
(LPARAM) psi);
return S_OK;
}
//----------------------------------------------------------------------
// RegisterCallback - The SIP is providing the IM with the pointer to
// the IIMCallback interface.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::RegisterCallback (
IIMCallback __RPC_FAR *lpIMCallback) {
m_pIMCallback = lpIMCallback;
PostMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_REGCALLBACK,
(LPARAM)m_pIMCallback);
return S_OK;
}
//----------------------------------------------------------------------
// GetImData - An application is passing IM-specific data to the IM.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::GetImData (DWORD dwSize,
LPVOID pvImData) {
return E_FAIL;
}
//----------------------------------------------------------------------
// SetImData - An application is querying IM-specific data from the IM.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::SetImData (DWORD dwSize,
LPVOID pvImData) {
return S_OK;
}
//----------------------------------------------------------------------
// UserOptionsDlg - The SIP Control Panel applet is asking for a
// configuration dialog box to be displayed.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::UserOptionsDlg (
HWND hwndParent) {
MessageBox (hwndParent, TEXT ("UserOptionsDlg called."),
TEXT ("NumPanel"), MB_OK);
return S_OK;
}
//----------------------------------------------------------------------
// SetIMMActiveContext - Provides information about the IME
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::SetIMMActiveContext(HWND hwnd,
BOOL bOpen, DWORD dwConversion,
DWORD dwSentence, DWORD hkl) {
return S_OK;
}
//----------------------------------------------------------------------
// RegisterCallback2 - The SIP is providing the IM with the pointer to
// the IIMCallback interface.
//
HRESULT STDMETHODCALLTYPE MyIInputMethod::RegisterCallback2 (
IIMCallback2 __RPC_FAR *lpIMCallback) {
m_pIMCallback = lpIMCallback;
PostMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_REGCALLBACK2,
(LPARAM)m_pIMCallback);
return S_OK;
}


NumPanel.cpp
//======================================================================
// NumPanel - An IM window
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#define COBJMACROS
#include <sip.h> // SIP includes
#include <keybd.h> // Keyboard flag includes
#include "NumPanel.h" // Includes for this window
extern "C" HINSTANCE hInst;
//
// Local variables for number panel
//
TCHAR g_tcBtnChar[] = {
TEXT('1'), TEXT('2'), TEXT('3'), TEXT('-'), TEXT('*'),
TEXT('4'), TEXT('5'), TEXT('6'), TEXT('+'), TEXT('/'),
TEXT('7'), TEXT('8'), TEXT('9'), TEXT('0'), TEXT('='),
};
UINT g_BtnVChars[] = {
'1', '2', '3', VK_HYPHEN, VK_MULTIPLY,
'4', '5', '6', VK_ADD, VK_SLASH,
'7', '8', '9', '0', VK_EQUAL,
};
// Message dispatch table for SipWindowProc
const struct decodeUINT SipMessages[] = {
WM_CREATE, DoCreateSip,
WM_PAINT, DoPaintSip,
MYMSG_METHCALL, DoSetSipInfo,
WM_LBUTTONDOWN, DoMouseSip,
WM_MOUSEMOVE, DoMouseSip,
WM_LBUTTONUP, DoMouseSip,
WM_LBUTTONDBLCLK, DoMouseSip,
WM_DESTROY, DoDestroySip,
};
//----------------------------------------------------------------------
// CreateIMWindow - Create the input method window.
//
HWND CreateIMWindow (HWND hwndParent) {
WNDCLASS wc;
RECT rect;
HWND hwnd;
// Register sip window class.
memset (&wc, 0, sizeof (wc));
wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = NPWndProc; // Callback function
wc.hInstance = hInst; // Owner handle
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszClassName = MYSIPCLS; // Window class name
if (RegisterClass (&wc) == 0) return 0;

// Create SIP window.
GetClientRect (hwndParent, &rect);
hwnd = CreateWindowEx (0, MYSIPCLS, TEXT("),
WS_VISIBLE | WS_CHILD | WS_BORDER, rect.left,
rect.top, rect.right - rect.left,
rect.bottom - rect.top, hwndParent, (HMENU)100,
hInst, 0);
if (!IsWindow (hwnd))
return 0;
return hwnd;
}
//----------------------------------------------------------------------
// DestroyIMWindow - Destroy the input method window.
//
int DestroyIMWindow (HWND hwnd) {
// Clean up since we're about to be unloaded.
DestroyWindow (hwnd);
UnregisterClass (MYSIPCLS, hInst);
return 0;
}
//======================================================================
// NPWndProc - Window procedure for SIP
//
LRESULT CALLBACK NPWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
// Call routine to handle control message.
for (i = 0; i < dim(SipMessages); i++) {
if (wMsg == SipMessages[i].Code)
return (*SipMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateSip - Process WM_CREATE message for window.
//
LRESULT CALLBACK DoCreateSip (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
LPSIPWNDSTRUCT pWndData;
// Allocate a data structure for the sip keyboard window.
pWndData = (LPSIPWNDSTRUCT)LocalAlloc (LPTR, sizeof (SIPWNDSTRUCT));
if (!pWndData) {
DestroyWindow (hWnd);
return 0;
}
memset (pWndData, 0, sizeof (SIPWNDSTRUCT));
GetWindowRect (GetParent (hWnd), &pWndData->rectDocked);
pWndData->rectLast.left = -1;
SetWindowLong (hWnd, GWL_USERDATA, (INT)pWndData);
return 0;
}
//----------------------------------------------------------------------
// DoSetSipInfo - Process set information user message for window.
//
LRESULT CALLBACK DoSetSipInfo (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
LPSIPWNDSTRUCT pWndData;
RECT rect;
pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA);
switch (wParam) {
// Called when RegisterCallback method called
case MSGCODE_REGCALLBACK:
pWndData->pIMCallback = (IIMCallback *)lParam;
break;
// Called when GetInfo method called
case MSGCODE_GETINFO:
pWndData->imi = *(IMINFO *)lParam;
break;
// Called when ReceiveSipInfo method called
case MSGCODE_SETINFO:
GetClientRect (GetParent(hWnd), &rect);
MoveWindow (hWnd, 0, 0, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
break;
// Called when RegisterCallback2 method called
case MSGCODE_REGCALLBACK2:
pWndData->pIMCallback2 = (IIMCallback2 *)lParam;
break;
}
return 0;
}
//----------------------------------------------------------------------
// DoPaintSip - Process WM_PAINT message for window.
//
LRESULT CALLBACK DoPaintSip (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
HDC hdc;
HBRUSH hOld;
PAINTSTRUCT ps;
RECT rect, rectBtn;
INT i, j, k, x, y, cx, cy, cxBtn, cyBtn;
LPSIPWNDSTRUCT pWndData;

pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA);
hdc = BeginPaint (hWnd, &ps);
GetClientRect (hWnd, &rect);
cx = (rect.right - rect.left - 3 - GRIPWIDTH) / CXBTNS;
cy = (rect.bottom - rect.top - 3) / CYBTNS;
cxBtn = cx - 3;
cyBtn = cy - 3;
// Select a brush for the gripper.
hOld = (HBRUSH)SelectObject (hdc, GetStockObject (GRAY_BRUSH));
Rectangle (hdc, rect.left, rect.top, rect.left + GRIPWIDTH,
rect.bottom);
SelectObject (hdc, hOld);
k = 0;
y = 3;
for (i = 0; i < CYBTNS; i++) {
x = 3 + GRIPWIDTH;
for (j = 0; j < CXBTNS; j++) {
SetRect (&rectBtn, x, y, x + cxBtn, y + cyBtn);
DrawButton (hdc, &rectBtn, &g_tcBtnChar[k],
pWndData->dwBtnDnFlags & (1 << k));
k++;
x += cx;
}
y += cy;
}
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------------
// ComputeFloatRect - Compute the location and size of the drag rect.
//
int ComputeFloatRect (HWND hwnd, LPSIPWNDSTRUCT pWndData, POINT pt,
RECT *prectOut) {
pt.x -= pWndData->ptMovBasis.x;
pt.y -= pWndData->ptMovBasis.y;
prectOut->right = FLOATWIDTH;
prectOut->bottom = FLOATHEIGHT;
prectOut->left = pt.x;
prectOut->top = pt.y;
prectOut->right += pt.x;
prectOut->bottom += pt.y;
return 0;
}
//----------------------------------------------------------------------
// DrawFloatRect - Draw a drag rectangle by XORing the desktop.
//
int DrawFloatRect (HWND hWnd, RECT rect) {
HDC hdc;
HBRUSH hbrOld;
HPEN hpenOld;
int nOldMode;
// Get the DC. Set ROP, brush, and pen.
hdc = GetDC (NULL);
nOldMode = SetROP2 (hdc, R2_NOT);
hbrOld = (HBRUSH)SelectObject (hdc, GetStockObject (NULL_BRUSH));
hpenOld = (HPEN)SelectObject (hdc, GetStockObject (BLACK_PEN));
Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom);
SelectObject (hdc, hbrOld);
SelectObject (hdc, hpenOld);
SetROP2 (hdc, nOldMode);
ReleaseDC (NULL, hdc);
return 0;
}
//----------------------------------------------------------------------
// HandleGripper - Handles mouse messages over gripper bar
//
LRESULT HandleGripper (HWND hWnd, LPSIPWNDSTRUCT pWndData, UINT wMsg,
LPARAM lParam) {
POINT pt;
RECT rectFloat;
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
switch (wMsg) {
case WM_LBUTTONDOWN:
if (pt.x > GRIPWIDTH+3)
return 0;
SetCapture (hWnd);
pWndData->fMoving = TRUE;
pWndData->ptMovBasis = pt;
ClientToScreen (hWnd, &pt);
pWndData->ptMovStart = pt;
ShowWindow (GetParent(hWnd), SW_HIDE);
break;
case WM_MOUSEMOVE:
if (!pWndData->fMoving)
return 0;
ClientToScreen (hWnd, &pt);
ComputeFloatRect (hWnd, pWndData, pt, &rectFloat);
// Erase old drag rectangle.
if (pWndData->rectLast.left != -1)
DrawFloatRect (hWnd, pWndData->rectLast);
// Draw new drag rectangle.
DrawFloatRect (hWnd, rectFloat);
pWndData->rectLast = rectFloat;
break;
case WM_LBUTTONUP:
if (!pWndData->fMoving)
return 0;
// Free up dragging stuff.
ReleaseCapture();
pWndData->fMoving = FALSE;
ClientToScreen (hWnd, &pt);
// Erase last drag rectangle.
ComputeFloatRect (hWnd, pWndData, pt, &rectFloat);
if (pWndData->rectLast.left != -1)
DrawFloatRect (hWnd, pWndData->rectLast);
pWndData->rectLast.left = -1;
ShowWindow (GetParent(hWnd), SW_SHOW);
// Don't move SIP if really small move.
if ((abs (pWndData->ptMovStart.x - pt.x) < 3) &&
(abs (pWndData->ptMovStart.y - pt.y) < 3))
break;
// Tell the Input Manager about the move.
pWndData->imi.rcSipRect = rectFloat;
pWndData->imi.fdwFlags &= ~SIPF_DOCKED;
pWndData->imi.fdwFlags |= SIPF_ON;
pWndData->pIMCallback->SetImInfo(&pWndData->imi);
break;
case WM_LBUTTONDBLCLK:
if (pt.x > GRIPWIDTH+3)
return 0;
ReleaseCapture();
pWndData->fMoving = FALSE;
// Embedded SIP manager doesn't use SIPF_DOCKED so only use on PPC.
#if defined(WIN32_PLATFORM_PSPC)
pWndData->imi.fdwFlags |= (SIPF_DOCKED | SIPF_ON);
#endif
pWndData->imi.rcSipRect = pWndData->rectDocked;
pWndData->pIMCallback->SetImInfo(&pWndData->imi);
break;
}
pWndData->dwBtnDnFlags = 0; // If we moved, no buttons down.
return 1;
}
//----------------------------------------------------------------------
// DoMouseSip - Process mouse button messages for window. WM_LBUTTONDOWN
//
LRESULT CALLBACK DoMouseSip (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
RECT rect;
INT i, x, y, cx, cy;
UINT nChar, unShiftFlags = 0;
DWORD BtnDnFlags;
LPSIPWNDSTRUCT pWndData;
pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA);
// See if moving gripper or gripper tap.
if (HandleGripper (hWnd, pWndData, wMsg, lParam))
return 0;
// Compute the button grid.
GetClientRect (hWnd, &rect);
cx = (rect.right - rect.left - 3 - GRIPWIDTH) / CXBTNS;
cy = (rect.bottom - rect.top - 3) / CYBTNS;
x = ((LOWORD (lParam)-3-GRIPWIDTH) / cx);
y = ((HIWORD (lParam)-3) / cy);
i = (y * CXBTNS) + x; // i now contains btn index.
// Do small amount of message-specific processing.
switch (wMsg) {
case WM_LBUTTONDOWN:
SetCapture (hWnd);
// Fall through to WM_MOUSEMOVE case.
case WM_MOUSEMOVE:
BtnDnFlags = 1 << i;
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONUP:
if (pWndData->dwBtnDnFlags)
ReleaseCapture();
BtnDnFlags = 0;
nChar = g_tcBtnChar[i];
pWndData->pIMCallback->SendCharEvents(g_BtnVChars[i],
KeyStateDownFlag, 1, &nShiftFlags, &nChar);
break;
}
// Decide how to repaint wnd. If only 1 btn changed, just
// invalidate that rect. Otherwise, invalidate entire wnd.
if ((wMsg == WM_MOUSEMOVE) && (BtnDnFlags !=pWndData->dwBtnDnFlags))
InvalidateRect (hWnd, NULL, FALSE);
else {
i = 3+GRIPWIDTH; // Compensate for the gripper on left side.
SetRect (&rect, x*cx+i, y*cy, (x+1)*cx+i, (y+1)*cy);
InvalidateRect (hWnd, &rect, FALSE);
}
pWndData->dwBtnDnFlags = BtnDnFlags;
return 0;
}
//----------------------------------------------------------------------
// DoDestroySip - Process WM_DESTROY message for window.
//
LRESULT CALLBACK DoDestroySip (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
LPSIPWNDSTRUCT pWndData;
pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA);
LocalFree (pWndData);
return 0;
}
//---------------------------------------------------------------------
// DrawButton - Draws a button
//
INT DrawButton (HDC hdc, RECT *prect, LPTSTR pChar, BOOL fPressed) {
if (!fPressed) {
SelectObject (hdc, GetStockObject (BLACK_PEN));
SelectObject (hdc, GetStockObject (WHITE_BRUSH));
SetBkColor (hdc, RGB (255, 255, 255));
SetTextColor (hdc, RGB (0, 0, 0));
} else {
SelectObject (hdc, GetStockObject (BLACK_BRUSH));
SelectObject (hdc, GetStockObject (WHITE_PEN));
SetTextColor (hdc, RGB (255, 255, 255));
SetBkColor (hdc, RGB (0, 0, 0));
}
Rectangle (hdc, prect->left, prect->top, prect->right,
prect->bottom);
Rectangle (hdc, prect->left+1, prect->top+1, prect->right+1,
prect->bottom+1);
DrawText (hdc, pChar, 1, prect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
return 0;
}











Although NumPanel is divided into two source files, next chapter, I turn the discussion to the Smartphone and the communication features common across the Smartphone and the Pocket PC Phone edition.

/ 169