_SUBCLASSING APPLICATIONS_ by Mike Klein [LISTING ONE] # Standard Windows make file. The utility MAKE.EXE compares the # creation date of the file to the left of the colon with the file(s) # to the right of the colon. If the file(s) on the right are newer # then the file on the left, Make will execute all of the command lines # following this line that are indented by at least one tab or space. # Any valid MS-DOS command line may be used. # This line allows NMAKE to work as well all: progedit.exe # Update the resource if necessary progedit.res: progedit.rc progedit.h progedit.ico rc -r progedit.rc # Update the object file if necessary progedit.obj: progedit.c progedit.h cl -W4 -c -AS -Gsw -Oad -Zp progedit.c # Update the executable file if necessary, and if so, add the resource back in. progedit.exe: progedit.obj progedit.def link /NOD progedit,,, libw slibcew, progedit.def rc progedit.res # If the .res file is new and the .exe file is not, update the resource. # Note that the .rc file can be updated without having to either # compile or link the file. progedit.exe: progedit.res rc progedit.res [LISITNG TWO] #define IDC_TABAMT 100 int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int); LONG FAR PASCAL MyMainWndProc(HWND, unsigned, WORD, LONG); BOOL FAR PASCAL TabAmount(HWND, unsigned, WORD, LONG); [LISTING THREE] ; module-definition file for Progeddit -- used by LINK.EXE NAME PROGEDIT ; application's module name DESCRIPTION 'Progedit - Programming Editor' EXETYPE WINDOWS ; required for all Windows applications STUB 'WINSTUB.EXE' ; Generates error message if application ; is run without Windows ;CODE can be moved in memory and discarded/reloaded CODE PRELOAD MOVEABLE DISCARDABLE ;DATA must be MULTIPLE if program can be invoked more than once DATA PRELOAD MOVEABLE MULTIPLE DISCARDABLE HEAPSIZE 1024 STACKSIZE 5120 ; recommended minimum for Windows applications ; All functions that will be called by any Windows routine ; MUST be exported. EXPORTS MyMainWndProc @1 TabAmount @2 [LISTING FOUR] #include #include "progedit.h" ProgEdit ICON PROGEDIT.ICO TabAmount DIALOG 11, 25, 75, 24 CAPTION "Tab Amount" STYLE WS_POPUPWINDOW | WS_CAPTION BEGIN CONTROL "Tab Amt:", -1, "static", SS_RIGHT | WS_CHILD, 10, 6, 30, 12 CONTROL "4", IDC_TABAMT, "edit", ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD, 45, 6, 20, 12 END [LISTING FIVE] /***************************************************************************** PROGRAM: ProgEdit -- AUTHOR: Mike Klein -- VERSION: 1.0 FILE: progedit.exe -- REQUIREMENTS: Windows 3.x PURPOSE: Example of adding a menu item to a "foreign" application. In this case, the program is Windows' NotePad, and the extension added is a definable tab stop setting to Notepad's menu bar. *****************************************************************************/ #define _WINDOWS #define NOCOMM #include #include #include #include "progedit.h" /* Handles & vars needed for ProgEdit */ HANDLE hInstProgEdit; FARPROC lpfnMyMainWndProc; /* Handles & vars needed for Notepad */ HMENU hMenuNotepad; HWND hWndNotepadMain; HWND hWndNotepadEdit; FARPROC lpfnNotepadMainWndProc; int TabAmt; BYTE Text[100]; /***************************************************************************** FUNCTION: WinMain PURPOSE : Calls initialization function, processes message loop *****************************************************************************/ int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; struct { WORD wAlwaysTwo; WORD wHowShown; } HowToShow; struct { WORD wEnvSeg; LPSTR lpCmdLine; LPVOID lpCmdShow; DWORD dwReserved; } ParameterBlock; HowToShow.wAlwaysTwo = 2; HowToShow.wHowShown = SW_SHOWNORMAL; ParameterBlock.wEnvSeg = 0; ParameterBlock.lpCmdLine = ""; ParameterBlock.lpCmdShow = (LPVOID) &HowToShow; ParameterBlock.dwReserved = NULL; hInstProgEdit = hInstance; /* Run a copy of NotePad */ if(LoadModule("notepad.exe", (LPVOID) &ParameterBlock) < 32) { MessageBox(NULL, "Running instance of NotePad", "ERROR", MB_OK | MB_ICONSTOP); return(FALSE); } /* Get handles to Notepad's two main windows */ hWndNotepadMain = GetActiveWindow(); hWndNotepadEdit = GetFocus(); /* Set up different function pointers. Get a ptr to my hWnd func, then ** plug it into the other application's struct so it calls my func. Of ** course, at end of my func, I call func that I stole in first place. */ lpfnMyMainWndProc=MakeProcInstance((FARPROC) MyMainWndProc, hInstProgEdit); lpfnNotepadMainWndProc=(FARPROC) SetWindowLong(hWndNotepadMain,GWL_WNDPROC, (DWORD) lpfnMyMainWndProc); /* Get handle to Notepad's menu and add Tabs to main menu */ hMenuNotepad = GetMenu(hWndNotepadMain); AppendMenu(hMenuNotepad, MF_STRING, IDC_TABAMT, "&Tabs"); DrawMenuBar(hWndNotepadMain); /* Read in tab amt from win.ini */ GetProfileString("ProgEdit", "Tabs", "4", Text, 2); TabAmt = (HIWORD(GetDialogBaseUnits()) * (Text[0] - '0')) / 4; SendMessage(hWndNotepadEdit, EM_SETTABSTOPS, 1, (LONG) (LPINT) &TabAmt); /* Acquire and dispatch messages until a WM_QUIT message is received. */ while(GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } FreeProcInstance(lpfnMyMainWndProc); return(FALSE); } /***************************************************************************** FUNCTION: MyMainWndProc PURPOSE : Filter/replacement function for Notepad's MainWndProc() *****************************************************************************/ LONG FAR PASCAL MyMainWndProc(HWND hWnd,unsigned wMsg,WORD wParam,LONG lParam) { FARPROC lpProc; switch(wMsg) { case WM_COMMAND : switch(wParam) { case IDC_TABAMT : /* Set tab stops in edit window */ lpProc = MakeProcInstance(TabAmount, hInstProgEdit); DialogBox(hInstProgEdit, "TabAmount", hWnd, lpProc); FreeProcInstance(lpProc); break;; default : break; } break; case WM_DESTROY : SendMessage(hWndNotepadMain, WM_QUIT, 0, 0L); PostQuitMessage(0); break; default : break; } return(CallWindowProc(lpfnNotepadMainWndProc, hWnd, wMsg, wParam, lParam)); } /***************************************************************************** FUNCTION: TabAmount PURPOSE : Processes messages for edit window that gets tab amount *****************************************************************************/ BOOL FAR PASCAL TabAmount(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam) { switch(wMsg) { case WM_INITDIALOG : /* Display the current tab setting */ SendDlgItemMessage(hWnd, IDC_TABAMT, EM_LIMITTEXT, 1, 0L); SetDlgItemInt(hWnd, IDC_TABAMT, (TabAmt * 4) / HIWORD(GetDialogBaseUnits()), FALSE); return(TRUE); case WM_COMMAND : switch(wParam) { case IDOK : case IDCANCEL : /* Get number of tabs and calculate it in dialog units */ GetDlgItemText(hWnd, IDC_TABAMT, Text, sizeof(Text)); TabAmt = (HIWORD(GetDialogBaseUnits()) * (Text[0] - '0')) / 4; /* Set the tab stops in the edit window */ SendMessage(hWndNotepadEdit, EM_SETTABSTOPS, 1, (LONG) (LPINT) &TabAmt); InvalidateRect(hWndNotepadEdit, NULL, TRUE); UpdateWindow(hWndNotepadEdit); /* Save the tab amt in WIN.INI profile */ WriteProfileString("ProgEdit", "Tabs", Text); EndDialog(hWnd, TRUE); return(TRUE); default : break; } break; default : break; } return(FALSE); } [LISTING SIX] # Standard Windows make file. The utility MAKE.EXE compares the # creation date of the file to the left of the colon with the file(s) # to the right of the colon. If the file(s) on the right are newer # then the file on the left, Make will execute all of the command lines # following this line that are indented by at least one tab or space. # Any valid MS-DOS command line may be used. # This line allows NMAKE to work as well all: subclass.exe # Update the resource if necessary subclass.res: subclass.rc subclass.h subclass.ico rc -r subclass.rc # Update the object file if necessary subclass.obj: subclass.c subclass.h cl -W4 -c -AS -Gsw -Oad -Zip subclass.c # Update the executable file if necessary, and if so, add the resource back in. subclass.exe: subclass.obj subclass.def link /NOD /CO subclass,,, libw slibcew, subclass.def rc subclass.res # If the .res file is new and the .exe file is not, update the resource. # Note that the .rc file can be updated without having to either # compile or link the file. subclass.exe: subclass.res rc subclass.res [LISITNG SEVEN] /* Standard defines */ #define FIRST (0L) #define LAST (0x7fff7fffL) #define ALL (0x00007fffL) #define IDC_LISTBOX 100 #define IDC_INPUTBOX 100 /* Function prototypes */ int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int); LONG FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG); LONG FAR PASCAL HandleListBoxes(HWND, unsigned, WORD, LONG); LONG FAR PASCAL HandleEditCtrls(HWND, unsigned, WORD, LONG); VOID PASCAL CloseEditWindow(VOID); VOID PASCAL OpenEditWindow(DWORD); [LISTING EIGHT] ; module-definition file for Megaphone -- used by LINK.EXE NAME Test ; application's module name DESCRIPTION 'Test' EXETYPE WINDOWS ; required for all Windows applications STUB 'WINSTUB.EXE' ; Generates error message if application ; is run without Windows ;CODE can be moved in memory and discarded/reloaded CODE PRELOAD MOVEABLE DISCARDABLE ;DATA must be MULTIPLE if program can be invoked more than once DATA PRELOAD MOVEABLE MULTIPLE HEAPSIZE 1024 STACKSIZE 5120 ; recommended minimum for Windows applications ; All functions that will be called by any Windows routine ; MUST be exported. EXPORTS MainWndProc @1 HandleListBoxes @2 HandleEditCtrls @3 [LISTING NINE] /* Include files needed for .RC file */ #include "windows.h" #include "subclass.h" /* The program's icon (not that it needs one) */ SubClass ICON SUBCLASS.ICO /* Main dialog w/listbox used by SubClass */ SubClass DIALOG 36, 34, 100, 100 CAPTION "SubClass" CLASS "SubClass" STYLE WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | DS_LOCALEDIT BEGIN CONTROL "", IDC_LISTBOX, "BetterListBox", LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_BORDER | WS_VSCROLL | WS_CHILD, 10, 10, 80, 80 END [LISTING TEN] /***************************************************************************** PROGRAM: SubClass -- AUTHOR: Mike Klein -- VERSION: 1.0 FILE : subclass.exe -- REQUIREMENTS: Windows 3.x PURPOSE: An example of a subclassed listbox, providing enhanced input and data-entry facilities. *****************************************************************************/ /* Some std defines needed */ #define _WINDOWS #define NOCOMM /* INCLUDE files */ #include #include "subclass.h" /* Global variables */ HANDLE hInstSubClass; HWND hDlgSubClass; HWND hWndListBox; HWND hWndEdit; int CurrentIndex; int NumListBoxItems; RECT CurrentItemRect; BOOL InsideEditMode = FALSE; DWORD dwEditPos; BYTE InputString[50]; /* Far pointers to Windows' class functions for listboxes and edit ctrls */ FARPROC lpfnListBox; FARPROC lpfnEditCtrl; /***************************************************************************** FUNCTION: WinMain PURPOSE : Calls initialization function, processes message loop *****************************************************************************/ int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; MSG msg; if(!hPrevInstance) { hInstSubClass = hInstance; /* Fill in window class structure with parameters that describe the ** main window */ wc.style = CS_DBLCLKS; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hInstSubClass; wc.hIcon = LoadIcon(hInstSubClass, "SubClass"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "SubClass"; if(!RegisterClass(&wc)) return(FALSE); /* Fill in window class structure with parameters that describe our ** custom list box -- BetterListBox */ wc.style = CS_DBLCLKS; wc.lpfnWndProc = HandleListBoxes; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstSubClass; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "BetterListBox"; if(!RegisterClass(&wc)) return(FALSE); /* Fill in window class structure with parameters that describe our ** custom edit control -- BetterEditCtrl */ wc.style = CS_DBLCLKS; wc.lpfnWndProc = HandleEditCtrls; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstSubClass; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_IBEAM); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "BetterEditCtrl"; if(!RegisterClass(&wc)) return(FALSE); /* Get information on listbox class, so we can find out what its ** class window function is. */ if(GetClassInfo(NULL, "listbox", &wc) == FALSE) return(FALSE); else lpfnListBox = (FARPROC) wc.lpfnWndProc; /* Get information on edit control class, so we can find out what its ** class window function is. */ if(GetClassInfo(NULL, "edit", &wc) == FALSE) return(FALSE); else lpfnEditCtrl = (FARPROC) wc.lpfnWndProc; /* Create the main window */ if((hDlgSubClass = CreateDialog(hInstSubClass, "SubClass", NULL, 0L)) == NULL) { return(FALSE); } /* Get an oft used handle */ hWndListBox = GetDlgItem(hDlgSubClass, IDC_LISTBOX); /* Put in some test strings. */ SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "computer"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "telephone"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "lcd"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "ochessica"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "heeyah"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "video"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "smoke"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "sky"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "lovely"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "windows"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "nunez"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "beer"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "pug"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "query"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "remote"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "party"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "mixer"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "skate"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "varied"); SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "interests"); /* Give the listbox an initial selection */ SendMessage(hWndListBox, LB_SETCURSEL, 0, 0L); ShowWindow(hDlgSubClass, nCmdShow); UpdateWindow(hDlgSubClass); } else { /* If there was another instance of SubClass running, then switch ** to it by finding any window of class = "SubClass". Then, if it's ** an icon, open the window, otherwise just make it active. */ hDlgSubClass = FindWindow("SubClass", NULL); if(IsIconic(hDlgSubClass)) ShowWindow(hDlgSubClass, SW_SHOWNORMAL); SetActiveWindow(hDlgSubClass); return(FALSE); } /* Acquire and dispatch messages until a WM_QUIT message is received. */ while(GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /***************************************************************************** FUNCTION: MainWndProc PURPOSE : Processes messages for SubClass dialog box *****************************************************************************/ LONG FAR PASCAL MainWndProc(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam) { switch(wMsg) { case WM_CLOSE : DestroyWindow(hDlgSubClass); return(0L); case WM_SETFOCUS : SetFocus(hWndListBox); return(0L); case WM_DESTROY : PostQuitMessage(0); return(0L); default : break; } return(DefDlgProc(hWnd, wMsg, wParam, lParam)); } /***************************************************************************** FUNCTION: OpenEditWindow PURPOSE : Opens edit window inside listbox *****************************************************************************/ VOID PASCAL OpenEditWindow(DWORD CharSel) { /* Flag telling us were in edit mode */ InsideEditMode = TRUE; /* Find out current index into listbox */ CurrentIndex = (int) SendMessage(hWndListBox, LB_GETCURSEL, 0, 0L); if(CurrentIndex == LB_ERR) CurrentIndex = 0; /* Find out what the text is in selected listbox cell */ SendMessage ( hWndListBox, LB_GETTEXT, CurrentIndex, (LONG) (LPSTR) InputString ); /* Get client dimensions of listbox cell with respect to the entire ** listbox and create an edit window right inside of it. */ SendMessage ( hWndListBox, LB_GETITEMRECT, CurrentIndex, (DWORD) (LPRECT) &CurrentItemRect ); hWndEdit = CreateWindow ( "BetterEditCtrl", "", ES_AUTOHSCROLL | ES_LEFT | WS_VISIBLE | WS_CHILD, CurrentItemRect.left + 2, CurrentItemRect.top, CurrentItemRect.right - CurrentItemRect.left - 2, CurrentItemRect.bottom - CurrentItemRect.top, hWndListBox, IDC_INPUTBOX, hInstSubClass, 0L ); /* Pre-fill the edit control with what was in the listbox cell */ SetWindowText(hWndEdit, InputString); SetFocus(hWndEdit); SendMessage(hWndEdit, EM_SETSEL, 0, CharSel); } /***************************************************************************** FUNCTION: CloseEditWindow PURPOSE : Closes edit window inside listbox *****************************************************************************/ VOID PASCAL CloseEditWindow(VOID) { /* Flag telling us were aren't in edit mode anymore */ InsideEditMode = FALSE; /* Get text of what was entered into edit control */ GetWindowText(hWndEdit, InputString, sizeof(InputString)); if(!GetWindowTextLength(hWndEdit)) { DestroyWindow(hWndEdit); SendMessage(hWndListBox, WM_KEYDOWN, VK_DELETE, 0L); return; } /* Turn redrawing for the listbox off. */ SendMessage(hWndListBox, WM_SETREDRAW, 0, 0L); /* Find out the RECT of the currently selected listbox item */ SendMessage(hWndListBox, LB_GETITEMRECT, CurrentIndex, (DWORD) (LPRECT) &CurrentItemRect); /* Delete the old string and add the new one */ SendMessage(hWndListBox, LB_INSERTSTRING, CurrentIndex, (LONG) (LPSTR) InputString); SendMessage(hWndListBox, LB_DELETESTRING, CurrentIndex + 1, 0L); /* Destroy the old edit window. */ DestroyWindow(hWndEdit); /* Validate the whole listbox and then invalidate only the list box rect ** that we put the edit window into. */ ValidateRect(hWndListBox, NULL); InvalidateRect(hWndListBox, &CurrentItemRect, TRUE); /* Turn re-drawing for the listbox back on and send a WM_PAINT for the ** entry changes to take effect. */ SendMessage(hWndListBox, WM_SETREDRAW, 1, 0L); UpdateWindow(hWndListBox); SetFocus(hWndListBox); } /***************************************************************************** FUNCTION: HandleListBoxes PURPOSE : Process keystrokes and mouse for list boxes *****************************************************************************/ LONG FAR PASCAL HandleListBoxes(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam) { switch(wMsg) { case WM_LBUTTONDBLCLK : /* Go into edit mode and put caret at end of edit ctrl. */ if(SendMessage(hWnd, LB_GETCOUNT, 0, 0L)) { OpenEditWindow(LAST); } return(0L); case WM_LBUTTONDOWN : if(InsideEditMode) { /* Find out cursor pos from the edit ctrl, so we can ** use same positioning for cell were moving into */ dwEditPos = SendMessage(hWndEdit, EM_GETSEL, 0, 0L); CloseEditWindow(); /* Tell listbox to move cur ptr up or down based on ** the mouse position, and open a new edit window */ SendMessage(hWndListBox, wMsg, wParam, lParam); OpenEditWindow ( MAKELONG(LOWORD(dwEditPos), LOWORD(dwEditPos)) ); return(0L); } break; case WM_MBUTTONDBLCLK : case WM_RBUTTONDBLCLK : /* Make middle & right mouse buttons like left mouse button. */ SendMessage(hWnd, WM_LBUTTONDBLCLK, wParam, lParam); break; case WM_MBUTTONDOWN : case WM_RBUTTONDOWN : /* Make middle & right mouse buttons like left mouse button. */ SendMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam); SendMessage(hWnd, WM_LBUTTONUP, wParam, lParam); break; case WM_KEYDOWN : switch(wParam) { case VK_RETURN : /* Enter was pressed, so go into edit mode and put the ** caret at the end of the edit ctrl */ if(SendMessage(hWnd, LB_GETCOUNT, 0, 0L)) { OpenEditWindow(LAST); } return(0L); case VK_INSERT : /* The INS key (add a new string). First, get currently ** selected entry. If none exists, assume that focus is on ** the first cell */ CurrentIndex = (int) SendMessage(hWnd, LB_GETCURSEL, 0, 0L); if(CurrentIndex == LB_ERR) { CurrentIndex = 0; } /* Find out what the text is in selected listbox cell */ if(SendMessage(hWnd, LB_GETCOUNT, 0, 0L)) { SendMessage ( hWnd, LB_GETTEXT, CurrentIndex, (LONG) (LPSTR) InputString ); } else { /* If nothing's in the listbox, then copy a null to ** the edit control. */ InputString[0] = '\0'; } /* Insert new entry */ SendMessage ( hWnd, LB_INSERTSTRING, CurrentIndex, (LONG) (LPSTR) InputString ); /* Let our "edit current cell" function take over */ OpenEditWindow(ALL); return(0L); case VK_DELETE : /* The DEL key. If no items are in the listbox, then ** return. Else, get the currently selected item. */ if(!(NumListBoxItems = (int) SendMessage(hWnd, LB_GETCOUNT, 0, 0L))) { break; } if((CurrentIndex = (int) SendMessage(hWnd, LB_GETCURSEL, 0, 0L)) == LB_ERR) { CurrentIndex = 0; } /* Delete the string. Tried to get rid of annoying ** focus rect; couldn't. Too many inconsitencies in ** the way Windows handles list and combo boxes. */ SendMessage(hWnd, LB_DELETESTRING, CurrentIndex, 0L); if(CurrentIndex == NumListBoxItems - 1) { --CurrentIndex; } /* Reset our listbox selection. */ SendMessage(hWnd, LB_SETCURSEL, CurrentIndex, 0L); return(0L); default : break; } break; default : break; } /* Return any unprocessed messages to window's original class procedure. */ return(CallWindowProc(lpfnListBox, hWnd, wMsg, wParam, lParam)); } /***************************************************************************** FUNCTION: HandleEditCtrls PURPOSE : Process keystrokes and mouse for edit controls *****************************************************************************/ LONG FAR PASCAL HandleEditCtrls(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam) { switch(wMsg) { case WM_LBUTTONDBLCLK : /* Turn of edit mode, closing the edit window */ CloseEditWindow(); return(0L); case WM_KEYDOWN : switch(wParam) { case VK_RETURN : /* Turn of edit mode, closing the edit window */ CloseEditWindow(); return(0L); case VK_DELETE : /* Delete a character if one exists in the edit ctrl. ** Otherwise, if the cell is blank, delete the entire ** cell. */ if(!GetWindowTextLength(hWnd)) { CloseEditWindow(); SendMessage(hWndListBox, wMsg, wParam, lParam); return(0L); } break; case VK_DOWN : case VK_UP : case VK_PRIOR : case VK_NEXT : /* Find out cursor pos from edit ctrl, so we can ** use same positioning for cell we're moving into */ dwEditPos = SendMessage(hWndEdit, EM_GETSEL, 0, 0L); CloseEditWindow(); /* Tell listbox to move cur ptr up or down, and ** open an edit window at the new pos. */ SendMessage(hWndListBox, wMsg, wParam, lParam); OpenEditWindow ( MAKELONG(LOWORD(dwEditPos), LOWORD(dwEditPos)) ); return(0L); default : break; } break; default : break; } /* Return any unprocessed messages to window's original class procedure. */ return(CallWindowProc(lpfnEditCtrl, hWnd, wMsg, wParam, lParam)); }