/*
- 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 <stdlib.h>
#include <string.h>
#include <windows.h>
#include <windowsx.h>
#ifdef _WIN32
#include <objerror.h>
#include <objbase.h>
#endif
#ifdef WIN16
#include <compobj.h>
#endif
#include <mapiwin.h>
#include <mapidbg.h>
#include <mapi.h>
#include <mapix.h>
#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;
}