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