|
|
/*
* 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); } }
|