/* - B I T M A P . C - * Purpose: * Bitmap and Listbox support functions for InBox in sample mail client. * * Copyright 1993-1995 Microsoft Corporation. All Rights Reserved. */ #include #include #include #include #ifdef _WIN32 #include #include #endif #ifdef WIN16 #include #endif #include #include #include #include #include "bitmap.h" #include "client.h" // Fonts to use in dialogs #ifdef _WIN32 #define SHELL_FONT "MS Shell Dlg" #define SHELL_FONT_SIZE 8 #else #define SHELL_FONT "MS Sans Serif" #define SHELL_FONT_SIZE 8 #endif /* * globals */ DWORD rgbWindowColor = 0xFF000000; // variables for the current DWORD rgbHiliteColor = 0xFF000000; // system color settings. DWORD rgbWindowText = 0xFF000000; // on a WM_SYSCOLORCHANGE DWORD rgbHiliteText = 0xFF000000; // we check to see if we need DWORD rgbGrayText = 0xFF000000; // to reload our bitmap. DWORD rgbDDWindow = 0xFF000000; // DWORD rgbDDHilite = 0xFF000000; // 0xFF000000 is an invalid RGB // an array of integers containing the tab stops, in pixels. The tab // stops must be sorted in ascending order; back tabs are not allowed. int rgTabs[] = { 2, 28, 135, 292 }; int dxbmpLB, dybmpLB; // dx and dy of listbox bmps HDC hdcMemory = 0; // hdc to hold listbox bitmaps (for speed) HBITMAP hbmpOrigMemBmp = 0; // original null bitmap in hdcMemory HBITMAP hbmpLB = 0; // cached listbox bitmaps HFONT hfontLB = 0; // hfont of LB HWND hwndLB = 0; // hwnd of LB FONTSTYLE fontStyle = { SHELL_FONT_SIZE, FW_NORMAL, 0, TEXT(SHELL_FONT) }; extern HANDLE hInst; /* - DeInitBmps - * Purpose: * cleans up LB hfonts, hdc, and hbmps */ VOID DeInitBmps(VOID) { DeleteBitmapLB(); if(hdcMemory) { DeleteDC(hdcMemory); hdcMemory = 0; } if(hfontLB) { SetWindowFont(hwndLB, GetStockObject(SYSTEM_FONT), FALSE); DeleteObject(hfontLB); hfontLB = 0; } } /* - SetLBFont - * Purpose: * creates a font from the global fontStyle * sets global hfontLB to new font and WM_SETFONTs * the hwndLB to the new font */ VOID SetLBFont(VOID) { LOGFONT lf; lf.lfHeight = fontStyle.lfHeight; lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = fontStyle.lfWeight; lf.lfItalic = fontStyle.lfItalic; lf.lfUnderline = 0; lf.lfStrikeOut = 0; lf.lfCharSet = ANSI_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS; lstrcpy(lf.lfFaceName, fontStyle.lfFaceName); hfontLB = CreateFontIndirect(&lf); if(hfontLB) SetWindowFont(hwndLB, hfontLB, FALSE); } /* - InitBmps - * Purpose: * inits listbox globals, creates listbox * * Arguments: * HWND main hwnd of app (parent of LB) * * Returns: * TRUE - success; FALSE - failed */ BOOL InitBmps(HWND hwnd, int idLB) { HDC hdcScreen; HBITMAP hbmpTemp; hdcScreen = GetDC(0); if(!hdcScreen) goto CantInit; hdcMemory = CreateCompatibleDC(hdcScreen); if(!hdcMemory) goto ReleaseScreenDC; hbmpTemp = CreateCompatibleBitmap(hdcMemory, 1, 1); if(!hbmpTemp) goto ReleaseMemDC; hbmpOrigMemBmp = SelectObject(hdcMemory, hbmpTemp); // get hbmp of NULL if(!hbmpOrigMemBmp) // bmp for hdcMemory goto ReleaseMemDC; // for when we delete SelectObject(hdcMemory, hbmpOrigMemBmp); // it later in life DeleteObject(hbmpTemp); ReleaseDC(0, hdcScreen); SetRGBValues(); // set the global RGB values LoadBitmapLB(); // load the bmps into hdcMemory hwndLB = GetDlgItem(hwnd, idLB); SetLBFont(); // set the font of our listbox return TRUE; /* Error recovery exits */ ReleaseMemDC: DeleteDC(hdcMemory); hdcMemory = 0; ReleaseScreenDC: ReleaseDC(0, hdcScreen); CantInit: return FALSE; } /* - SetRGBValues - * Purpose: * To set various system colors in static variables. Called at * init time and when system colors change. */ VOID SetRGBValues(VOID) { rgbWindowColor = GetSysColor(COLOR_WINDOW); rgbHiliteColor = GetSysColor(COLOR_HIGHLIGHT); rgbWindowText = GetSysColor(COLOR_WINDOWTEXT); rgbHiliteText = GetSysColor(COLOR_HIGHLIGHTTEXT); rgbGrayText = GetSysColor(COLOR_GRAYTEXT); } /* - MeasureItem - * Purpose: * called from msg WM_MEASUREITEM: returns max dy of listbox items * * Arguments: * HWND hwnd of main window * pmis measureitemstruct from WM_MEASUREITEM call */ VOID MeasureItem(HANDLE hwnd, LPMEASUREITEMSTRUCT pmis) { HDC hDC = GetDC(hwnd); HANDLE hFont = hfontLB; TEXTMETRIC TM; if(!hFont) hFont = GetStockObject(SYSTEM_FONT); hFont = SelectObject(hDC, hFont); GetTextMetrics(hDC, &TM); SelectObject(hDC, hFont); ReleaseDC(hwnd, hDC); // set the height to be max of (dyfont or dybitmap) pmis->itemHeight = max(dybmpLB, TM.tmHeight); } /* - OutTextFormat - * Purpose: * to parse the string in the listbox and draw it accordingly: * first char == chBOLD: line is bold * first char == chUNDERLINE: line is underlined (can follow chBOLD) * char == chTAB: go to next column in rgTabs * '/001#': bitblt that numbered bitmap. * otherwise, outtext the line * * Arguments: * pDI from DrawItem from WM_DRAWITEM msg */ VOID OutTextFormat(LPDRAWITEMSTRUCT pDI) { TCHAR szDateRec[32]; TCHAR szItem[256]; TCHAR szTemp[4]; TCHAR szDots[4] = {"..."}; TCHAR *pch; INT nT; INT nTab = 0; // current tab we is on INT nBmp; // index of envelope bitmap HFONT hfDef = 0; HFONT hfOld = 0; // bold or underlined font TCHAR *pchBuff = NULL; LPMSGID lpMsgId = (LPMSGID)pDI->itemData; pch = szItem; // Format a string from the info in lpMsgNode // First, calculate the index to the desired bitmap nBmp = ((!lpMsgId->fUnRead) * 2) + ((!!lpMsgId->fHasAttach) * 1 ); // Convert our received date and build string ConvertDateRec (lpMsgId->lpszDateRec, szDateRec); // Limit our subject size szTemp[0] = '\0'; if(lpMsgId->lpszSubject && (lstrlen(lpMsgId->lpszSubject) > 32)) { memcpy(szTemp, &lpMsgId->lpszSubject[28], 4); memcpy(&lpMsgId->lpszSubject[28], szDots, 4); } wsprintf(szItem, "\001%d\t%s\t%s\t%s", nBmp, (lpMsgId->lpszFrom ? lpMsgId->lpszFrom : ""), (lpMsgId->lpszSubject ? lpMsgId->lpszSubject : ""), szDateRec); // erase background ExtTextOut(pDI->hDC, 0, 0, ETO_OPAQUE, &pDI->rcItem, NULL, 0, NULL); // underline or bold this line? Only check first & second char if(*pch == chBOLD || *pch == chUNDERLINE) { LOGFONT lf; hfOld = GetWindowFont(pDI->hwndItem); if(!hfOld) hfOld = GetStockObject(SYSTEM_FONT); GetObject(hfOld, sizeof(lf), &lf); if(*pch == chBOLD) { lf.lfWeight = FW_BOLD; pch++; } if(*pch == chUNDERLINE) { lf.lfUnderline = TRUE; pch++; } hfDef = CreateFontIndirect(&lf); if(hfDef) SelectObject(pDI->hDC, hfDef); } // selected or nonselected bmps? nT = (ODS_SELECTED & pDI->itemState) ? (BMWIDTH * NUMBMPS) : 0; // parse the string for(; *pch; pch++) { TCHAR *pchT; RECT rc; if(*pch == chBITMAP) // do we have a bitmap? { ++pch; // draw the bitmap BitBlt(pDI->hDC, pDI->rcItem.left + rgTabs[nTab], pDI->rcItem.top, BMWIDTH, BMHEIGHT, hdcMemory, nT + (int)(*pch - TEXT('0')) * BMWIDTH, 0, SRCCOPY); continue; } if(*pch == chTAB) // move to next tabstop? { nTab++; continue; } pchT = pch; // find end of the column of text while(*pchT && (*pchT != chTAB)) pchT++; // set rect to drawtext in SetRect(&rc, pDI->rcItem.left + rgTabs[nTab], pDI->rcItem.top, pDI->rcItem.right, pDI->rcItem.bottom); // draw the text ExtTextOut(pDI->hDC, rc.left, rc.top + 1, ETO_OPAQUE | ETO_CLIPPED, &rc, pch, pchT - pch, NULL); pch = pchT - 1; // move to end of this column } if(hfDef) // delete underline or bold font if we created it { SelectObject(pDI->hDC, hfOld); DeleteObject(hfDef); } if(szTemp[0] != '\0') { memcpy(&lpMsgId->lpszSubject[28], szTemp, 4); } } /* - DrawItem - * Purpose: * Handles WM_DRAWITEM for both drive and directory listboxes. * * Parameters: * pDI LPDRAWITEMSTRUCT passed from the WM_DRAWITEM message. */ VOID DrawItem(LPDRAWITEMSTRUCT pDI) { COLORREF crText, crBack; if((int)pDI->itemID < 0) return; if((ODA_DRAWENTIRE | ODA_SELECT) & pDI->itemAction) { if(pDI->itemState & ODS_SELECTED) { // Select the appropriate text colors crText = SetTextColor(pDI->hDC, rgbHiliteText); crBack = SetBkColor(pDI->hDC, rgbHiliteColor); } // parse and spit out bmps and text OutTextFormat(pDI); // Restore original colors if we changed them above. if(pDI->itemState & ODS_SELECTED) { SetTextColor(pDI->hDC, crText); SetBkColor(pDI->hDC, crBack); } } if((ODA_FOCUS & pDI->itemAction) || (ODS_FOCUS & pDI->itemState)) DrawFocusRect(pDI->hDC, &pDI->rcItem); } /* - ConvertDateRec - * Purpose: * To convert the lpszDateReceived field of a message to a * more paletable display format; namely: mm/dd/yy hh:mmAM. * * Parameters: * lpszDateRec - Original format * lpszDateDisplay - Display format */ VOID ConvertDateRec (LPSTR lpszDateRec, LPSTR lpszDateDisplay) { char szDateTmp[32]; LPSTR lpszYear; LPSTR lpszMonth; LPSTR lpszDay; LPSTR lpszHour; LPSTR lpszMinute; int nHour; static char szFoo[2][3] = {"AM", "PM"}; *lpszDateDisplay = 0; if (!lpszDateRec || !*lpszDateRec) return; lstrcpy(szDateTmp, lpszDateRec); lpszYear = strtok (szDateTmp, "/ :"); lpszMonth = strtok (NULL, "/ :"); lpszDay = strtok (NULL, "/ :"); lpszHour = strtok (NULL, "/ :"); lpszMinute = strtok (NULL, "/ :"); if(lpszHour) nHour = atoi (lpszHour); else nHour = 0; if (nHour > 12) wsprintf (lpszHour, "%d", nHour - 12); wsprintf (lpszDateDisplay, "%s/%s/%s %s:%s%s", lpszMonth, (lpszDay ? lpszDay : ""), (lpszYear ? lpszYear : ""), (lpszHour ? lpszHour : ""), (lpszMinute ? lpszMinute : ""), szFoo[(nHour > 11 ? 1 : 0)]); } /* * RgbInvertRgb * * Purpose: * To reverse the byte order of the RGB value (for file format * * Arguments: * * Returns: * New color value (RGB to BGR) */ #define RgbInvertRgb(_rgbOld) \ (DWORD)RGB(GetBValue(_rgbOld), GetGValue(_rgbOld), GetRValue(_rgbOld)) /* * LoadAlterBitmap (mostly stolen from commdlg) * * Purpose: * Loads the IDB_ENVELOPE bitmap and gives all the pixels that are * RGBREPLACE a new color. * * Assumption: * This function will work on one bitmap during it's lifetime. * (Due to the fact that it finds RGBREPLACE once and then * operates on that offset whenever called again because under NT, * it appears that the bitmap is cached, so the second time you go * looking for RGBREPLACE, it won't be found.) You could load the * resource, copy it, then modify the copy as a workaround. But I * chose the cheap way out as I will only ever modify one bmp. * * Arguments: * rgbInstead rgb value to replace defined RGBREPLACE with * * Returns: * NULL - failed or hbmp of new modified bitmap */ HBITMAP LoadAlterBitmap(DWORD rgbInstead) { HANDLE hbmp = 0; LPBITMAPINFOHEADER qbihInfo; HDC hdcScreen; HRSRC hresLoad; HGLOBAL hres; LPBYTE qbBits; DWORD rgbReplace = 0; DWORD *rgdw = NULL; DWORD *lpdw = NULL; ULONG cb = 0; if (rgbInstead) rgbReplace = RGBREPLACE; // load our listbox bmps resource hresLoad = FindResource(hInst, MAKEINTRESOURCE(IDB_ENVELOPE), RT_BITMAP); if(hresLoad == 0) return 0; hres = LoadResource(hInst, hresLoad); if(hres == 0) return 0; rgbReplace = RgbInvertRgb(rgbReplace); rgbInstead = RgbInvertRgb(rgbInstead); qbihInfo = (LPBITMAPINFOHEADER)LockResource(hres); // Skip over the header structure qbBits = (LPBYTE)(qbihInfo + 1); // Skip the color table entries, if any qbBits += (1 << (qbihInfo->biBitCount)) * sizeof(RGBQUAD); // Copy the resource into writable memory so we can // munge the color table to set our background color cb = (ULONG)(qbBits - (LPBYTE)qbihInfo) + qbihInfo->biSizeImage; rgdw = (DWORD *)GlobalAllocPtr(GMEM_MOVEABLE, cb); CopyMemory((LPVOID)rgdw, (LPVOID)qbihInfo, cb); // find the color to replace in the color table for(lpdw = (DWORD *)((LPBYTE)rgdw + qbihInfo->biSize); ; lpdw++) { if(*lpdw == rgbReplace) break; } // replace that color value with our new one *lpdw = (DWORD)rgbInstead; // Create a color bitmap compatible with the display device hdcScreen = GetDC(0); if(hdcScreen != 0) { hbmp = CreateDIBitmap(hdcScreen, (LPBITMAPINFOHEADER)rgdw, (LONG)CBM_INIT, qbBits, (LPBITMAPINFO) rgdw, DIB_RGB_COLORS); ReleaseDC(0, hdcScreen); } UnlockResource(hres); FreeResource(hres); GlobalFreePtr(rgdw); return hbmp; } /* * DeleteBitmapLB * * Purpose: * Get rid of hbmpLB, if it exists */ VOID DeleteBitmapLB(VOID) { if(hbmpOrigMemBmp) { SelectObject(hdcMemory, hbmpOrigMemBmp); if(hbmpLB != 0) { DeleteObject(hbmpLB); hbmpLB = 0; } } } /* * LoadBitmapLB (mostly stolen from commdlg) * * Purpose: * Creates the listbox bitmap. If an appropriate bitmap * already exists, it just returns immediately. Otherwise, it * loads the bitmap and creates a larger bitmap with both regular * and highlight colors. * * Returns: * TRUE - success; FALSE - failure */ BOOL LoadBitmapLB(VOID) { BITMAP bmp; HANDLE hbmp, hbmpOrig; HDC hdcTemp; BOOL bWorked = FALSE; // check for existing bitmap and validity if( (hbmpLB != 0) && (rgbWindowColor == rgbDDWindow) && (rgbHiliteColor == rgbDDHilite)) { if(SelectObject(hdcMemory, hbmpLB)) return TRUE; } DeleteBitmapLB(); rgbDDWindow = rgbWindowColor; rgbDDHilite = rgbHiliteColor; if(!(hdcTemp = CreateCompatibleDC(hdcMemory))) goto LoadExit; if(!(hbmp = LoadAlterBitmap(rgbWindowColor))) goto DeleteTempDC; GetObject(hbmp, sizeof(BITMAP), (LPBYTE) &bmp); dybmpLB = bmp.bmHeight; dxbmpLB = bmp.bmWidth; hbmpOrig = SelectObject(hdcTemp, hbmp); hbmpLB = CreateDiscardableBitmap(hdcTemp, dxbmpLB*2, dybmpLB); if(!hbmpLB) goto DeleteTempBmp; if(!SelectObject(hdcMemory, hbmpLB)) { DeleteBitmapLB(); goto DeleteTempBmp; } BitBlt(hdcMemory, 0, 0, dxbmpLB, dybmpLB, // copy unhighlited bmps hdcTemp, 0, 0, SRCCOPY); // into hdcMemory SelectObject(hdcTemp, hbmpOrig); DeleteObject(hbmp); if(!(hbmp = LoadAlterBitmap(rgbHiliteColor))) goto DeleteTempDC; hbmpOrig = SelectObject(hdcTemp, hbmp); BitBlt(hdcMemory, dxbmpLB, 0, dxbmpLB, dybmpLB, // copy highlited bmps hdcTemp, 0, 0, SRCCOPY); // into hdcMemory SelectObject(hdcTemp, hbmpOrig); bWorked = TRUE; DeleteTempBmp: DeleteObject(hbmp); DeleteTempDC: DeleteDC(hdcTemp); LoadExit: return bWorked; }