windows-nt/Source/XPSP1/NT/sdktools/gutils/status.c
2020-09-26 16:20:57 +08:00

893 lines
23 KiB
C

/*
* status line handler
*
*/
/*---includes-----------------------------------------------------------*/
#include "windows.h"
#include "string.h"
#include "gutils.h"
/* --- data structures ------------------------------------------------- */
#define SF_MAXLABEL 80 /* no more than 80 in an item within the bar */
/* Is this adequate for long pathnames on a
hi-res screen?
*/
typedef struct statel {
int type; /* SF_BUTTON or SF_STATIC */
int flags; /* SF_VAR => variable width
SF_LEFT=> left aligned (else right)
SF_RAISE=> paint as 'raised' 3d rect
SF_LOWER=> paint as lowered 3D rect
SF_SZMIN=>together with SF_VAR
allows minimum size for
var sized item
SF_SZMAX=>see SZMIN and use nouse
*/
int id; /* control id */
int width; /* width of control in chars */
char text[SF_MAXLABEL+1]; /* null-term string for label */
RECT rc; /* used by status.c */
} STATEL, * PSTATEL;
typedef struct itemlist {
int nitems;
PSTATEL statels;
int selitem; /* used by status.c */
BOOL isselected; /* used by status.c */
} ILIST, * PILIST;
/* ------------------------------------------------------------------*/
/* prototypes of routines in this module */
void StatusCreateTools(void);
void StatusDeleteTools(void);
INT_PTR APIENTRY StatusWndProc(HWND, UINT, WPARAM, LPARAM);
void StatusResize(HWND hWnd, PILIST pilist);
int StatusCalcHeight(HWND hWnd, PSTATEL ip);
int StatusCalcWidth(HWND hWnd, PSTATEL ip);
PSTATEL StatusGetItem(PILIST plist, int id);
void LowerRect(HDC hDC, LPRECT rcp);
void RaiseRect(HDC hDC, LPRECT rcp);
void StatusPaint(HWND hWnd, PILIST iplistp);
void BottomRight(HDC hDC, LPRECT rcp, HPEN hpen, BOOL bCorners);
void TopLeft(HDC hDC, LPRECT rcp, HPEN hpen, BOOL bCorners);
void StatusButtonDown(HDC hDC, PSTATEL ip);
void StatusButtonUp(HDC hDC, PSTATEL ip);
void InitDC(HDC hdc);
/*--global data---------------------------------------------------------*/
HPEN hpenHilight, hpenLowlight;
HPEN hpenBlack, hpenNeutral;
HBRUSH hbrBackground; /* pieces and board */
HFONT hFont;
int status_charheight, status_charwidth;
/* default pt size for font (tenths of a pt) */
#define DEF_PTSIZE 80
/*-public functions----------------------------------------------------------*/
/* StatusInit
*
* - create window class
*/
BOOL
StatusInit(
HANDLE hInstance
)
{
WNDCLASS wc;
BOOL resp;
TEXTMETRIC tm = {0};
HDC hDC;
StatusCreateTools();
wc.style = CS_HREDRAW|CS_VREDRAW|CS_GLOBALCLASS;
wc.lpfnWndProc = StatusWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(HANDLE);
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = hbrBackground;
wc.lpszClassName = (LPSTR) "gdstatusclass";
wc.lpszMenuName = NULL;
resp = RegisterClass(&wc);
hDC = GetDC(NULL);
if (hDC)
{
InitDC(hDC);
GetTextMetrics(hDC, &tm);
ReleaseDC(NULL, hDC);
}
else
{
// arbitrary, whatever...
tm.tmHeight = 14;
tm.tmAveCharWidth = 5;
}
status_charheight = (int)(tm.tmHeight + tm.tmExternalLeading);
status_charwidth = (int)tm.tmAveCharWidth;
return(resp);
}
/*
* create and show the window
*/
HWND APIENTRY
StatusCreate(
HANDLE hInst,
HWND hParent,
INT_PTR id,
LPRECT rcp,
HANDLE hmem
)
{
HWND hWnd;
/* create a child window of status class */
hWnd = CreateWindow("gdstatusclass",
NULL,
WS_CHILD | WS_VISIBLE,
rcp->left,
rcp->top,
(rcp->right - rcp->left),
(rcp->bottom - rcp->top),
hParent,
(HANDLE) id,
hInst,
(LPVOID) hmem);
return(hWnd);
}
/* return default height of this window */
int APIENTRY
StatusHeight(
HANDLE hmem
)
/* The window has a number of items which are arranged horizontally,
so the window height is the maximum of the individual heights
*/
{
PILIST plist;
int i;
int sz;
int maxsize = 0;
plist = (PILIST) GlobalLock(hmem);
if (plist != NULL) {
for (i = 0; i<plist->nitems; i++) {
sz = StatusCalcHeight(NULL, &plist->statels[i]);
maxsize = max(sz, maxsize);
}
}
GlobalUnlock(hmem);
if (maxsize > 0) {
return(maxsize + 4);
} else {
return(status_charheight + 4);
}
}
/* alloc the plist struct and return handle to caller */
HANDLE
StatusAlloc(
int nitems
)
{
HANDLE hmem;
PILIST pilist;
LPSTR chp;
hmem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,
sizeof(ILIST) + (sizeof(STATEL) * nitems));
chp = GlobalLock(hmem);
if (chp == NULL) {
return(NULL);
}
pilist = (PILIST) chp;
pilist->nitems = nitems;
pilist->statels = (PSTATEL) &chp[sizeof(ILIST)];
GlobalUnlock(hmem);
return(hmem);
}
/* insert an item into the plist */
BOOL
StatusAddItem(
HANDLE hmem,
int itemnr,
int type,
int flags,
int id,
int width,
LPSTR text
)
{
PILIST pilist;
PSTATEL pel;
pilist = (PILIST) GlobalLock(hmem);
if ((pilist == NULL) || (itemnr >= pilist->nitems)) {
GlobalUnlock(hmem);
return(FALSE);
}
pel = &pilist->statels[itemnr];
pel->type = type;
pel->flags = flags;
pel->id = id;
pel->width = width;
if (text == NULL) {
pel->text[0] = '\0';
} else {
lstrcpy(pel->text, text);
}
GlobalUnlock(hmem);
return(TRUE);
}
/* ---- internal functions ------------------------------------------*/
void
InitDC(HDC hdc)
{
SetBkColor(hdc, RGB(192,192,192));
SelectObject(hdc, hbrBackground);
SelectObject(hdc, hFont);
}
void
StatusCreateTools()
{
LOGFONT lf;
HDC hdc;
int scale;
hbrBackground = CreateSolidBrush(RGB(192,192,192));
hpenHilight = CreatePen(0, 1, RGB(255, 255, 255));
hpenLowlight = CreatePen(0, 1, RGB(128, 128, 128));
hpenNeutral = CreatePen(0, 1, RGB(192, 192, 192));
hpenBlack = CreatePen(0, 1, RGB(0, 0, 0));
hdc = GetDC(NULL);
if (hdc)
{
scale = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
}
else
{
// arbitrary, whatever...
scale = 72;
}
lf.lfHeight = -MulDiv(DEF_PTSIZE, scale, 720);
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = FW_REGULAR;
lf.lfItalic = 0;
lf.lfUnderline = 0;
lf.lfStrikeOut = 0;
lf.lfCharSet = ANSI_CHARSET;
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfQuality = PROOF_QUALITY;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
lf.lfFaceName[0] = '\0';
#ifdef COMPLEX
hFont = CreateFontIndirect(&lf);
#else
hFont = GetStockObject(SYSTEM_FONT);
#endif
}
void
StatusDeleteTools()
{
DeleteObject(hbrBackground);
DeleteObject(hpenHilight);
DeleteObject(hpenLowlight);
DeleteObject(hpenBlack);
DeleteObject(hpenNeutral);
#ifdef COMPLEX
DeleteObject(hFont);
#endif
}
/* Main winproc for status windows
*
* handles create/destroy and paint requests
*/
INT_PTR
StatusWndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
HANDLE hitems;
PSTATEL ip;
PILIST plist;
CREATESTRUCT * cp;
int i;
HDC hDC;
RECT rc;
POINT pt;
switch (message) {
case WM_CREATE:
cp = (CREATESTRUCT *) lParam;
hitems = (HANDLE) cp->lpCreateParams;
SetWindowLongPtr(hWnd, 0, (LONG_PTR)hitems);
plist = (PILIST) GlobalLock(hitems);
if (plist != NULL) {
plist->selitem = -1;
GlobalUnlock(hitems);
}
break;
case WM_SIZE:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
plist = (PILIST) GlobalLock(hitems);
if (plist != NULL) {
StatusResize(hWnd, plist);
GlobalUnlock(hitems);
}
break;
case WM_PAINT:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
plist = (PILIST) GlobalLock(hitems);
StatusPaint(hWnd, plist);
GlobalUnlock(hitems);
break;
case WM_LBUTTONUP:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
plist = (PILIST) GlobalLock(hitems);
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
if (plist == NULL) {
break;
}
if (plist->selitem != -1) {
ip = &plist->statels[plist->selitem];
if (plist->isselected) {
hDC = GetDC(hWnd);
if (hDC)
{
InitDC(hDC);
StatusButtonUp(hDC, ip);
ReleaseDC(hWnd, hDC);
}
}
plist->selitem = -1;
ReleaseCapture();
if (PtInRect(&ip->rc, pt)) {
SendMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(ip->id, WM_LBUTTONUP), (LPARAM)hWnd);
}
}
GlobalUnlock(hitems);
break;
case WM_LBUTTONDOWN:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
plist = (PILIST) GlobalLock(hitems);
if (plist == NULL) {
break;
}
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
if (plist->selitem == -1) {
for (i = 0; i< plist->nitems; i++) {
ip = &plist->statels[i];
if (PtInRect(&ip->rc, pt)) {
if (ip->type != SF_BUTTON) {
break;
}
plist->selitem = i;
SetCapture(hWnd);
plist->isselected = TRUE;
hDC = GetDC(hWnd);
if (hDC)
{
InitDC(hDC);
StatusButtonDown(hDC, ip);
ReleaseDC(hWnd, hDC);
}
break;
}
}
}
GlobalUnlock(hitems);
break;
case WM_MOUSEMOVE:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
plist = (PILIST) GlobalLock(hitems);
if (plist == NULL) {
break;
}
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
if (plist->selitem != -1) {
ip = &plist->statels[plist->selitem];
if (PtInRect(&ip->rc, pt)) {
if (!plist->isselected) {
hDC = GetDC(hWnd);
if (hDC)
{
InitDC(hDC);
StatusButtonDown(hDC, ip);
ReleaseDC(hWnd, hDC);
}
plist->isselected = TRUE;
}
} else {
if (plist->isselected) {
hDC = GetDC(hWnd);
if (hDC)
{
InitDC(hDC);
StatusButtonUp(hDC, ip);
ReleaseDC(hWnd, hDC);
}
plist->isselected = FALSE;
}
}
}
GlobalUnlock(hitems);
break;
case WM_DESTROY:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
GlobalUnlock(hitems);
GlobalFree(hitems);
SetWindowLongPtr(hWnd, 0, 0);
break;
case SM_NEW:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
if (hitems != NULL) {
GlobalFree(hitems);
}
hitems = (HANDLE) wParam;
if (hitems == NULL) {
SetWindowLongPtr(hWnd, 0, 0);
InvalidateRect(hWnd, NULL, TRUE);
break;
}
plist = (PILIST) GlobalLock(hitems);
if (plist == NULL) {
SetWindowLongPtr(hWnd, 0, 0);
InvalidateRect(hWnd, NULL, TRUE);
break;
}
plist->selitem = -1;
StatusResize(hWnd, plist);
GlobalUnlock(hitems);
SetWindowLongPtr(hWnd, 0, (LONG_PTR)hitems);
InvalidateRect(hWnd, NULL, TRUE);
break;
case SM_SETTEXT:
hitems = (HANDLE) GetWindowLongPtr(hWnd, 0);
if (hitems == NULL) {
break;
}
plist = (PILIST) GlobalLock(hitems);
ip = StatusGetItem(plist, (int)wParam);
if (ip != NULL) {
if (lParam == 0) {
ip->text[0] = '\0';
} else {
My_mbsncpy(ip->text, (LPSTR) lParam, SF_MAXLABEL);
ip->text[SF_MAXLABEL] = '\0';
}
/* if this is a variable width field, we need to redo
* all size calcs in case the field width has changed.
* in that case, we need to repaint the entire window
* and not just this field - so set rc to indicate the
* area to be redrawn.
*/
if (ip->flags & SF_VAR) {
StatusResize(hWnd, plist);
GetClientRect(hWnd, &rc);
RedrawWindow(hWnd, &rc, NULL,
RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW);
} else {
/* instead of just invalidating the window, we can
* force the window to be repainted now. This is
* essential for status updates during a busy
* loop when no messages are being processed,
* but we should still update the user on what's
* happening.
*/
RedrawWindow(hWnd, &ip->rc, NULL,
RDW_INVALIDATE|RDW_NOERASE|RDW_UPDATENOW);
}
}
GlobalUnlock(hitems);
break;
default:
return(DefWindowProc(hWnd, message, wParam, lParam));
}
return 0;
}
/*
* position the labels and buttons within the status window */
void
StatusResize(HWND hWnd, PILIST iplistp)
{
RECT rc;
int curpos_right, curpos_left;
int height, width;
int i;
PSTATEL ip;
if (iplistp == NULL) {
return;
}
GetClientRect(hWnd, &rc);
curpos_left = rc.left + status_charwidth / 2;
curpos_right = rc.right - (status_charwidth / 2);
/* loop through all items setting their position rects.
* items are flagged as being left or right. We place them
* in order starting at the left and the right, with a single
* char's width between each item
*/
for (i = 0; i < iplistp->nitems; i++) {
ip = &iplistp->statels[i];
width = StatusCalcWidth(hWnd, ip);
height = StatusCalcHeight(hWnd, ip);
ip->rc.top = (rc.bottom - height) / 2;
ip->rc.bottom = ip->rc.top + height;
/* see if this item fits. Items that partially fit
* are placed reduced in size.
*/
if (ip->flags & SF_LEFT) {
if (curpos_left+width >= curpos_right) {
/* doesn't completely fit-does it partly? */
if ((curpos_left + 1) >= curpos_right) {
/* no - this item does not fit */
ip->rc.left = 0;
ip->rc.right = 0;
} else {
/* partial fit */
ip->rc.left = curpos_left;
ip->rc.right = curpos_right - 1;
curpos_left = curpos_right;
}
} else {
/* complete fit */
ip->rc.left = curpos_left;
ip->rc.right = curpos_left + width;
curpos_left += width + 1;
}
} else {
/* same size check for right-aligned items */
if (curpos_right-width <= curpos_left) {
/* partial fit ? */
if (curpos_right <= curpos_left+1) {
ip->rc.left = 0;
ip->rc.right = 0;
} else {
/* yes - partial fit */
ip->rc.left = curpos_left + 1;
ip->rc.right = curpos_right;
curpos_right = curpos_left;
}
} else {
/* complete fit */
ip->rc.right = curpos_right;
ip->rc.left = curpos_right - width;
curpos_right -= (width + 1);
}
}
}
}
void
StatusPaint(HWND hWnd, PILIST iplistp)
{
RECT rc;
HDC hDC;
PAINTSTRUCT ps;
int i;
PSTATEL ip;
HPEN hpenOld;
GetClientRect(hWnd, &rc);
hDC = BeginPaint(hWnd, &ps);
InitDC(hDC);
RaiseRect(hDC, &rc);
if (iplistp == NULL) {
EndPaint(hWnd, &ps);
return;
}
for (i =0; i < iplistp->nitems; i++) {
ip = &iplistp->statels[i];
if (ip->rc.left == ip->rc.right) {
continue;
}
if (ip->type == SF_STATIC) {
if (ip->flags & SF_RAISE) {
RaiseRect(hDC, &ip->rc);
} else if (ip->flags & SF_LOWER) {
LowerRect(hDC, &ip->rc);
}
rc = ip->rc;
rc.left += (status_charwidth / 2);
rc.right--;
rc.top++;
rc.bottom--;
hpenOld = SelectObject(hDC, hpenNeutral);
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(hDC, hpenOld);
DrawText(hDC, ip->text, lstrlen(ip->text), &rc,
DT_LEFT | DT_VCENTER);
} else {
StatusButtonUp(hDC, ip);
}
}
EndPaint(hWnd, &ps);
}
void
RaiseRect(HDC hDC, LPRECT rcp)
{
TopLeft(hDC, rcp, hpenHilight, FALSE);
BottomRight(hDC, rcp, hpenLowlight, FALSE);
}
void
LowerRect(HDC hDC, LPRECT rcp)
{
TopLeft(hDC, rcp, hpenLowlight, FALSE);
BottomRight(hDC, rcp, hpenHilight, FALSE);
}
void
StatusButtonUp(HDC hDC, PSTATEL ip)
{
RECT rc;
HPEN hpenOld;
TEXTMETRIC tm;
rc = ip->rc;
TopLeft(hDC, &rc, hpenBlack, TRUE);
BottomRight(hDC, &rc, hpenBlack, FALSE);
rc.top++;
rc.bottom--;
rc.left++;
rc.right--;
TopLeft(hDC, &rc, hpenHilight, FALSE);
BottomRight(hDC, &rc, hpenLowlight, TRUE);
rc.top++;
rc.bottom--;
rc.left++;
rc.right--;
BottomRight(hDC, &rc, hpenLowlight, TRUE);
rc.bottom--;
rc.right--;
hpenOld = SelectObject(hDC, hpenNeutral);
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(hDC, hpenOld);
GetTextMetrics(hDC, &tm);
rc.top += tm.tmExternalLeading;
DrawText(hDC, ip->text, lstrlen(ip->text), &rc, DT_CENTER | DT_VCENTER);
}
void
StatusButtonDown(HDC hDC, PSTATEL ip)
{
RECT rc;
HPEN hpenOld;
TEXTMETRIC tm;
rc = ip->rc;
TopLeft(hDC, &rc, hpenBlack, TRUE);
BottomRight(hDC, &rc, hpenBlack, FALSE);
rc.top++;
rc.bottom--;
rc.left++;
rc.right--;
TopLeft(hDC, &rc, hpenLowlight, TRUE);
rc.top++;
rc.left++;
TopLeft(hDC, &rc, hpenNeutral, TRUE);
rc.top++;
rc.left++;
TopLeft(hDC, &rc, hpenNeutral, TRUE);
rc.top++;
rc.left++;
hpenOld = SelectObject(hDC, hpenNeutral);
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(hDC, hpenOld);
GetTextMetrics(hDC, &tm);
rc.top += tm.tmExternalLeading;
DrawText(hDC, ip->text, lstrlen(ip->text), &rc, DT_CENTER | DT_VCENTER);
}
void
TopLeft(HDC hDC, LPRECT rcp, HPEN hpen, BOOL bCorners)
{
HPEN hpenOld;
int x, y;
hpenOld = SelectObject(hDC, hpen);
x = rcp->right - 1;
y = rcp->bottom;
if (!bCorners) {
x--;
y--;
}
MoveToEx(hDC, x, rcp->top, NULL);
LineTo(hDC, rcp->left, rcp->top);
LineTo(hDC, rcp->left, y);
SelectObject(hDC, hpenOld);
}
void
BottomRight(HDC hDC, LPRECT rcp, HPEN hpen, BOOL bCorners)
{
HPEN hpenOld;
int x, y;
hpenOld = SelectObject(hDC, hpen);
x = rcp->left - 1;
y = rcp->top;
if (!bCorners) {
x++;
y++;
}
MoveToEx(hDC, rcp->right-1, y, NULL);
LineTo(hDC, rcp->right-1, rcp->bottom-1);
LineTo(hDC, x, rcp->bottom-1);
SelectObject(hDC, hpenOld);
}
PSTATEL
StatusGetItem(PILIST plist, int id)
{
int i;
if (plist == NULL) {
return(NULL);
}
for (i = 0; i < plist->nitems; i++) {
if (plist->statels[i].id == id) {
return(&plist->statels[i]);
}
}
return(NULL);
}
/*
* calculate the width of a given field. This is the width in characters
* multiplied by the average character width, plus a few units for
* borders.
*
* if SF_VAR is set, this field size varies depending on the text, so
* we use GetTextExtent for the field size. If SF_VAR is selected, the caller
* can specify that the size is not to exceed the (width * avecharwidth)
* size (using SF_SZMAX) or that it is not be less than it (SF_SZMIN).
*/
int
StatusCalcWidth(HWND hWnd, PSTATEL ip)
{
int ch_size, t_size;
SIZE sz = {0};
HDC hDC;
ch_size = ip->width * status_charwidth;
if (ip->flags & SF_VAR) {
hDC = GetDC(hWnd);
if (hDC)
{
InitDC(hDC);
GetTextExtentPoint(hDC, ip->text, lstrlen(ip->text), &sz);
ReleaseDC(hWnd, hDC);
}
t_size = sz.cx;
/*
* check this size against min/max size if
* requested
*/
if (ip->flags & SF_SZMIN) {
if (ch_size > t_size) {
t_size = ch_size;
}
}
if (ip->flags & SF_SZMAX) {
if (ch_size < t_size) {
t_size = ch_size;
}
}
ch_size = t_size;
}
if (ch_size != 0) {
if (ip->type == SF_BUTTON) {
return(ch_size+6);
} else {
return(ch_size+4);
}
} else {
return(0);
}
}
int
StatusCalcHeight(HWND hWnd, PSTATEL ip)
{
int size;
size = status_charheight;
if (ip->type == SF_BUTTON) {
return(size + 6);
} else {
return(size + 2);
}
}