|
-
May 23rd, 2004, 11:31 AM
#1
Switching focus from child window to child window
I just finished this program and one thing that was bothering me was a few nitpick things that made it work, I'm not sure how, maybe I'm just not looking at the problem correctly.
Code:
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
int idFocus = 0;
TCHAR szChildClass[] = TEXT("Checker4_Child");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Checker4");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if(!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires a Windows OS that supports Unicode."),
szAppName, MB_ICONERROR);
return 0;
}
wndclass.lpfnWndProc = ChildWndProc;
wndclass.cbWndExtra = sizeof(long);
wndclass.hIcon = NULL;
wndclass.lpszClassName = szChildClass;
if(!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("An error has occured while attempting to register a child window."),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Checker4 Mouse Hit-Test Demo"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// handles all of the little child windows together, however, does not control the user input
// to each child window
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// window handles for 25 child windows
static HWND hwndChild[DIVISIONS][DIVISIONS];
// none of the below values are static because they need to be used once whenever the
// WM_SIZE message is processed, they are not used whenever any other message is processed
// will hold the length of the client area
int cxBlock;
// will hold the height of the client area
int cyBlock;
// the below 2 values are mostly for loop iterations and keeping track of the focus box
// will hold the length of the child window
int x;
// will hold the height of the child window
int y;
switch(message)
{
case WM_CREATE:
for(x = 0; x < DIVISIONS; x++)
{
for(y = 0; y < DIVISIONS; y++)
{
// create 25 child windows
hwndChild[x][y] = CreateWindow(szChildClass, NULL, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU)(y << 8 | x),
// get the instance from the parent window using this method with these
// values passed into it
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
}
}
return 0;
case WM_SIZE:
// get the size of the entire client area in the parent window and divide it by 5,
// this will tell the average height and length of the child windows
cxBlock = LOWORD(lParam) / DIVISIONS;
cyBlock = HIWORD(lParam) / DIVISIONS;
for(x = 0; x < DIVISIONS; x++)
{
for(y = 0; y < DIVISIONS; y++)
{
// realign the child windows so that they would all fit within the client area
// of the parent window
MoveWindow(hwndChild[x][y], x * cxBlock, y * cyBlock, cxBlock, cyBlock, TRUE);
}
}
return 0;
// if the left-mouse-button is pressed somewhere in the main window, a error message beep
// will be given
case WM_LBUTTONDOWN:
MessageBeep(0);
return 0;
// On set-focus message, set focus to child window
case WM_SETFOCUS:
// get the focus from one child window to another
SetFocus(GetDlgItem(hwnd, idFocus));
return 0;
// On key-down message, possible change the focus window
case WM_KEYDOWN:
x = idFocus & 0xFF;
y = idFocus >> 8;
switch(wParam)
{
// move the focus square up
case VK_UP:
y--;
break;
// move the focus square down
case VK_DOWN:
y++;
break;
// move the focus square left
case VK_LEFT:
x--;
break;
// move the focus square right
case VK_RIGHT:
x++;
break;
// move the focus square all the way to the top and to the left
case VK_HOME:
x = 0;
y = 0;
break;
// move the focus square all the way to the bottom and to the right
case VK_END:
x = DIVISIONS - 1;
y = DIVISIONS - 1;
break;
default:
break;
}
// calculate the position where the
x = (x + DIVISIONS) % DIVISIONS;
y = (y + DIVISIONS) % DIVISIONS;
// move everything over by eight bits and add to itthe variable x
idFocus = y << 8 | x;
// set the focus to another child window from a previous one
SetFocus(GetDlgItem(hwnd, idFocus));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message)
{
// passes into the reserved memory a 0, indicating that an X is not to be drawn in a
// particular window
case WM_CREATE:
SetWindowLong(hwnd, 0, 0); // on/off flag
return 0;
case WM_KEYDOWN:
// send most key presses to the parent window
if(wParam != VK_RETURN && wParam != VK_SPACE)
{
// first value passed into the function is the window handle to the parent window
SendMessage(GetParent(hwnd), message, wParam, lParam);
return 0;
}
// for return and space fall through to toggle the square
case WM_RBUTTONDOWN:
case WM_LBUTTONDOWN:
// resets the the special value that is being stored
// in this case the value is xored with a 1 and if the original value was 0(no X is
// there from the outstart), the new value would be a 1
// however, if the previous value was a 1 then a xor with another 1 would yield a 0
// and the whole X would be erased across the child window
SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));
// sets the focus to this particular child window
SetFocus(hwnd);
// the entire window will be redrawn
InvalidateRect(hwnd, NULL, FALSE);
return 0;
// for focus messages, invalidate the window for repaint
case WM_SETFOCUS:
// get the id of the particular child window
idFocus = GetWindowLong(hwnd, GWL_ID);
// fall through
case WM_KILLFOCUS:
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
// draw the X
if(GetWindowLong(hwnd, 0))
{
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, rect.right, rect.bottom);
MoveToEx(hdc, 0, rect.bottom, NULL);
LineTo(hdc, rect.right, 0);
}
// draw the focus rectangle
if(hwnd == GetFocus())
{
rect.left += rect.right / 10;
rect.right -= rect.left;
rect.top += rect.bottom / 10;
rect.bottom -= rect.top;
// inbetween the dashes there will be nothing drawn
SelectObject(hdc, GetStockObject(NULL_BRUSH));
// a dashed line will outline a rectangle
SelectObject(hdc, CreatePen(PS_DASH, 0, 0));
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
My question is about the 2 different portions that I made bold.
---------------------------------------------------------------------------------
The first one. Since idFocus is initially 0. In the first line of the first bold portion, the entire thing is anded with a 0xFF. Why would one do that? I guess what I'm asking for is a nuts and bolts explanation.
Then in the first part on the second line, 8 bits are moved over to the right. Again why would you do that(in the context to the entire program)?
-------------------------------------------------------------------------------
On the second boldened part, 8 bits in y are moved to the left and then it uses or with x and re-assigns it to idFocus. Why?
----------------------------------------------------------------------------------
Thanks in advance for your comments, I'll keep at this problem for some time.
Favorite music:
Rammstein
E nomine
Prodigy
"Beer, the solution and the cause of all of our problems" -- Homer Simpson
-
May 23rd, 2004, 11:50 AM
#2
To retrieve the Lo and Hi order of the value? ... I guess so ...
-
May 23rd, 2004, 12:04 PM
#3
My firs theory on this was something like this.....
For the first boldened part...
---------------------------------------------------------------------------------
My initial thought was that you would first set the values in 'x' to some specific value(in this case 0xFF).
Then you would move the bytes that were originally stored in 'y' to the left, thereby getting rid of them entirely.
---------------------------------------------------------------------------------
For the second boldened part...
---------------------------------------------------------------------------------
You would move the bytes that were originally stored in 'y' in order to reveal 8 newfound blanks. Then by using the logical or ('|'), you would input some values from 'x' into 'idFocus'. That's atleast what Petzold said(atleast the way that I understood it).
---------------------------------------------------------------------------------
My question is why do you do this, how does it work exactly and how does windows interpret this information? Why were the bits moved over by 8 bytes and not by 2, 4, 16, 32? That's what I'm trying to get at, the nuts-and-bolts explanations why you need to slap these 2 values together, rather than having a function that gets the x and the y-coordinates and then resets the focus for the child windows?
Favorite music:
Rammstein
E nomine
Prodigy
"Beer, the solution and the cause of all of our problems" -- Homer Simpson
-
May 31st, 2004, 02:14 PM
#4
I asked Charles Petzold on the response and here it is...
By Charles Petzold
Each child window of a particular parent requires a unique child ID number.
This ID number is first assigned in the 9th argument to the CreateWindow
call. In this program, I thought it would be convenient to use an ID number
that could be easily converted to and from the X and Y coordinates of the
child window.
When I first wrote this program for Windows 1.0, the ID numbers were 16 bits
wide. (They are now 32 bits wide.) So, it made sense to put the store the
X coordinate in the lower 8 bits and the Y coordinate in the upper 8 bits.
If you have the X and Y coordinates, you can calculate an ID like this:
id = y << 8 | x
The Y coordinate is shifted 8 bits to the left and then ORed with the X.
Each pair of unique X and Y coordinates creates a unique ID. If you have an
ID, you can extract the X and Y coordinates like this:
x = id & 0xFF
y = id >> 8
The first statement retrieves only the X part of the ID; the second gets the
Y part. This is a very common technique in C to store multiple pieces of
information in a single variable.
I didn't need to shift by 8. I could have shifted by 7 or 6 or 5 or 4 or
even 3 bits. (If shifting by 3 bits, then you would use 0x07 to extract the
X coordinate from the ID. But I couldn't shift by just 2 because the number
of child windows horizontally and vertically is given by the DIVISIONS
identifier, which is assigned 5, and to store 5 of something you need at
least 3 bits.
The advantage of using 8 bits is that you can increase the DIVISIONS
identifier to anything up to 256 and recompile and the program will still
work. (Try it!) Versions of Windows prior to Windows 95 could only support
65,536 child windows, so that was the limit back then.
Another approach is to use multiplication and addition to create an ID from
X and Y:
id = y * DIVISIONS + x
You can then extract the X and Y coordinates like so:
x = id % DIVISIONS
y = id / DIVISIONS
However, it's generally best to use AND, OR, and shfits rather than
multiplication and division because they're more efficient.
The program initializes idFocus to 0, which means that the child with the
input focus is the one at the upper-left corner. In the WM_KEYDOWN message,
the program needs to obtain the X and Y coordinates of the child with the
input focus, change one of the those coordinates, and then reset the input
focus. It extracts the X and Y coordinates from idFocus, manipulates them,
then creates a new idFocus from these new coordinates.
Charles
I hope this helps someone out in the future .
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|