//============================================================================ // Copyright (c) 1996, Microsoft Corporation // // File: bubble.c // // History: // Abolade Gbadegesin Mar-1-1996 Created. // // This file contains code for the bubble-popup control. //============================================================================ #include #include #include #include #include #include #include "bpopup.h" // public declarations #include "bubble.h" // private declarations //---------------------------------------------------------------------------- // Function: BubblePopup_Init // // This function is called to initialize the control class. // It registers the bubble-popup window class. //---------------------------------------------------------------------------- BOOL BubblePopup_Init( IN HINSTANCE hinstance ) { // // if the window class is registered already, return // WNDCLASS wc; if (GetClassInfo(hinstance, WC_BUBBLEPOPUP, &wc)) { return TRUE; } // // set up the window class for registration // wc.lpfnWndProc = BP_WndProc; wc.hCursor = LoadCursor(hinstance, IDC_ARROW); wc.hIcon = NULL; wc.lpszMenuName = NULL; wc.hInstance = hinstance; wc.lpszClassName = WC_BUBBLEPOPUP; wc.hbrBackground = (HBRUSH)(COLOR_INFOBK + 1); wc.style = CS_HREDRAW | CS_VREDRAW; wc.cbWndExtra = sizeof(BPOPUP *); wc.cbClsExtra = 0; return RegisterClass(&wc); } //---------------------------------------------------------------------------- // Function: BP_WndProc // // This is the window procedure for all windows in the BubblePopup class. //---------------------------------------------------------------------------- LRESULT CALLBACK BP_WndProc( IN HWND hwnd, IN UINT uiMsg, IN WPARAM wParam, IN LPARAM lParam ) { BPOPUP *pbp; // // attempt to retrieve the private data pointer for the window // on WM_NCCREATE, this fails, so we allocate the data. // if ( NULL == hwnd) { return (LRESULT)FALSE; } pbp = BP_GetPtr(hwnd); if (pbp == NULL) { if (uiMsg != WM_NCCREATE) { return DefWindowProc(hwnd, uiMsg, wParam, lParam); } // // allocate a block of memory // pbp = (BPOPUP *)Malloc(sizeof(BPOPUP)); if (pbp == NULL) { return (LRESULT)FALSE; } // // save the pointer in the window's private bytes // pbp->hwnd = hwnd; // //Reset Error code, because BP_SetPtr won't reset the error code when //it succeeds // SetLastError( 0 ); if ((0 == BP_SetPtr(hwnd, pbp)) && (0 != GetLastError())) { Free(pbp); return (LRESULT)FALSE; } return DefWindowProc(hwnd, uiMsg, wParam, lParam); } // // if the window is being destroyed, free the block allocated // and set the private bytes pointer to NULL // if (uiMsg == WM_NCDESTROY) { Free(pbp); BP_SetPtr(hwnd, 0); return (LRESULT)0; } // // handle other messages // switch(uiMsg) { HANDLE_MSG(pbp, WM_CREATE, BP_OnCreate); HANDLE_MSG(pbp, WM_DESTROY, BP_OnDestroy); case WM_PAINT: { return BP_OnPaint(pbp); } case WM_WINDOWPOSCHANGED: { BP_ResizeClient(pbp); return 0; } case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: { // // hide the window if it is showing // BP_OnDeactivate(pbp); return 0; } case WM_GETFONT: { return (LRESULT)pbp->hfont; } case WM_SETFONT: { BOOL bRet = BP_OnSetFont(pbp, (HFONT)wParam, (BOOL)LOWORD(lParam)); BP_ResizeClient(pbp); if (pbp->dwFlags & BPFLAG_Activated) { InvalidateRect(pbp->hwnd, NULL, TRUE); UpdateWindow(pbp->hwnd); } return bRet; } case WM_SETTEXT: { // // change the text we're currently using, // and invalidate our client area // Free0(pbp->pszText); pbp->pszText = StrDup((PTSTR)lParam); BP_ResizeClient(pbp); if (pbp->dwFlags & BPFLAG_Activated) { InvalidateRect(pbp->hwnd, NULL, TRUE); UpdateWindow(pbp->hwnd); } return (pbp->pszText) ? TRUE : FALSE; } case WM_GETTEXT: { // // return the text we're currently using // PTSTR dst = (LPTSTR)lParam; PTSTR src = pbp->pszText; return lstrlen(lstrcpyn(dst, src ? src : TEXT(""), (int)wParam)); } case WM_TIMER: { BP_OnDeactivate(pbp); return 0; } case BPM_SETTIMEOUT: { pbp->uiTimeout = (UINT)lParam; if (pbp->dwFlags & BPFLAG_Activated) { KillTimer(pbp->hwnd, pbp->ulpTimer); pbp->ulpTimer = SetTimer( pbp->hwnd, BP_TimerId, pbp->uiTimeout, NULL ); } return 0; } case BPM_ACTIVATE: { return BP_OnActivate(pbp); } case BPM_DEACTIVATE: { return BP_OnDeactivate(pbp); } } return DefWindowProc(hwnd, uiMsg, wParam, lParam); } //---------------------------------------------------------------------------- // Function: BP_OnCreate // // This function handles the creation of private data for a bubble-popup. //---------------------------------------------------------------------------- BOOL BP_OnCreate( IN BPOPUP * pbp, IN CREATESTRUCT * pcs ) { // // initialize the structure members // pbp->iCtrlId = PtrToUlong(pcs->hMenu); pbp->pszText = (pcs->lpszName ? StrDup((PTSTR)pcs->lpszName) : NULL); pbp->dwFlags = 0; pbp->ulpTimer = 0; pbp->uiTimeout = 5000; // // we force the window to have the WS_POPUP style // SetWindowLong(pbp->hwnd, GWL_STYLE, WS_POPUP); // // set the WS_EX_TOOLWINDOW style to make sure // that this window doesn't show up in the tasklist // SetWindowLong(pbp->hwnd, GWL_EXSTYLE, pcs->dwExStyle | WS_EX_TOOLWINDOW); return BP_OnSetFont(pbp, NULL, FALSE); } //---------------------------------------------------------------------------- // Function: BP_OnDestroy // // This function handles the deallocation of private data for a bubble-popup. //---------------------------------------------------------------------------- VOID BP_OnDestroy( IN BPOPUP * pbp ) { // // if the font was created by this window, delete it // if (pbp->dwFlags & BPFLAG_FontCreated) { DeleteObject(pbp->hfont); } pbp->dwFlags = 0; pbp->hfont = NULL; } //---------------------------------------------------------------------------- // Function: BP_OnSetFont // // This function handles the changing of the font in use by a bubble-popup. //---------------------------------------------------------------------------- BOOL BP_OnSetFont( IN BPOPUP * pbp, IN HFONT hfont, IN BOOL bRedraw ) { if (pbp->dwFlags & BPFLAG_FontCreated) { DeleteObject(pbp->hfont); } pbp->dwFlags &= ~BPFLAG_FontCreated; pbp->hfont = NULL; if (!hfont) { // // (re)create the default font. // NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(ncm); if (!SystemParametersInfo( SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0 )) { TRACE1("error %d getting font info", GetLastError()); return FALSE; } hfont = CreateFontIndirect(&ncm.lfStatusFont); if (!hfont) { TRACE("error creating bubble-popup font"); return FALSE; } pbp->dwFlags |= BPFLAG_FontCreated; } pbp->hfont = hfont; if (bRedraw) { InvalidateRect(pbp->hwnd, NULL, TRUE); } return TRUE; } //---------------------------------------------------------------------------- // Function: BP_OnGetRect // // This function recomputes the rectangle required to display // a bubble-popup's current text. //---------------------------------------------------------------------------- VOID BP_OnGetRect( IN BPOPUP * pbp, IN RECT * prc ) { if (!pbp->pszText) { SetRectEmpty(prc); } else { HFONT hfontOld; HDC hdc = GetDC(pbp->hwnd); if (hdc) { // // select the font into the DC and compute the new rectangle // hfontOld = SelectObject(hdc, pbp->hfont); DrawText(hdc, pbp->pszText, -1, prc, DT_CALCRECT | DT_EXPANDTABS); if (hfontOld) { SelectObject(hdc, hfontOld); } ReleaseDC(pbp->hwnd, hdc); // // make space in the rectangle for the border // InflateRect( prc, GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE) ); } } // // convert the rectangle to screen coordinates // MapWindowPoints(pbp->hwnd, NULL, (POINT *)prc, 2); } //---------------------------------------------------------------------------- // Function: BP_ResizeClient // // When a change occurs (e.g. font-change, new text) this function is called // to resize the bubble-popup's window so the text still fits. //---------------------------------------------------------------------------- VOID BP_ResizeClient( IN BPOPUP * pbp ) { RECT rc; // // find out what size the window needs to be to hold // the text it is currently set to display // BP_OnGetRect(pbp, &rc); // // resize the window so its client area is large enough // to hold DrawText's output // SetWindowPos( pbp->hwnd, HWND_TOPMOST, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE ); } //---------------------------------------------------------------------------- // Function: BP_OnPaint // // This function handles the painting of a bubble-popup window. //---------------------------------------------------------------------------- DWORD BP_OnPaint( IN BPOPUP * pbp ) { HDC hdc; HBRUSH hbr; HFONT hfontOld; PAINTSTRUCT ps; RECT rc, rcText; if (!pbp->hfont || !pbp->pszText) { return (DWORD)-1; } hdc = BeginPaint(pbp->hwnd, &ps); GetClientRect(pbp->hwnd, &rc); rcText = rc; InflateRect( &rcText, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE) ); hfontOld = SelectObject(hdc, pbp->hfont); SetTextColor(hdc, GetSysColor(COLOR_INFOTEXT)); // // clear the window's background // hbr = CreateSolidBrush(GetSysColor(COLOR_INFOBK)); if (hbr) { FillRect(hdc, &rc, hbr); DeleteObject(hbr); } // // draw our formatted text in the window // SetBkMode(hdc, TRANSPARENT); DrawText(hdc, pbp->pszText, -1, &rcText, DT_EXPANDTABS); // // draw a border around the window // DrawEdge(hdc, &rc, BDR_RAISEDOUTER, BF_RECT); if (hfontOld) { SelectObject(hdc, hfontOld); } EndPaint(pbp->hwnd, &ps); return 0; } BOOL BP_OnActivate( IN BPOPUP * pbp ) { if (pbp->dwFlags & BPFLAG_Activated) { KillTimer(pbp->hwnd, pbp->ulpTimer); } ShowWindow(pbp->hwnd, SW_SHOW); UpdateWindow(pbp->hwnd); pbp->ulpTimer = SetTimer(pbp->hwnd, BP_TimerId, pbp->uiTimeout, NULL); pbp->dwFlags |= BPFLAG_Activated; return TRUE; } BOOL BP_OnDeactivate( IN BPOPUP * pbp ) { if (pbp->ulpTimer) { KillTimer(pbp->hwnd, pbp->ulpTimer); pbp->ulpTimer = 0; } ShowWindow(pbp->hwnd, SW_HIDE); pbp->dwFlags &= ~BPFLAG_Activated; return TRUE; }