504 lines
11 KiB
C
504 lines
11 KiB
C
#define OEMRESOURCE // setting this gets OBM_ constants in windows.h
|
|
#include <windows.h>
|
|
#include "ctls.h"
|
|
#pragma hdrstop
|
|
|
|
#define MyModuleHandle GetModuleHandle( NULL )
|
|
#define MYASSERT(x)
|
|
#define MyFree(x) LocalFree( x )
|
|
|
|
PWSTR DuplicateString( PWSTR pszOriginal )
|
|
{
|
|
PWSTR pszCopy;
|
|
|
|
pszCopy = LocalAlloc( LPTR, ( 1 + lstrlenW( pszOriginal ) ) * sizeof( *pszOriginal ) );
|
|
|
|
if ( NULL != pszCopy )
|
|
{
|
|
lstrcpy( pszCopy, pszOriginal );
|
|
}
|
|
|
|
return pszCopy;
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
//
|
|
// Bitmap control
|
|
//
|
|
////////////////////////////////////////////
|
|
|
|
PCWSTR szBMPCLASS = L"_mybmp";
|
|
|
|
|
|
LRESULT
|
|
BmpClassWndProc(
|
|
IN HWND hwnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
);
|
|
|
|
|
|
BOOL
|
|
InitializeBmpClass(
|
|
VOID
|
|
)
|
|
{
|
|
WNDCLASS wc;
|
|
BOOL b;
|
|
|
|
if(GetClassInfo(MyModuleHandle,szBMPCLASS,&wc)) {
|
|
b = TRUE;
|
|
} else {
|
|
|
|
wc.lpszClassName = szBMPCLASS;
|
|
wc.style = CS_GLOBALCLASS;
|
|
wc.lpfnWndProc = BmpClassWndProc;
|
|
wc.hInstance = MyModuleHandle;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
|
|
b = RegisterClass(&wc);
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyBmpClass(
|
|
VOID
|
|
)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
if(GetClassInfo(MyModuleHandle,szBMPCLASS,&wc)) {
|
|
//
|
|
// Hope there are no more windows using the class!
|
|
//
|
|
MYASSERT(!FindWindow(szBMPCLASS,NULL));
|
|
UnregisterClass(szBMPCLASS,MyModuleHandle);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
BmpClassPaint(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
unsigned BmpId;
|
|
HDC hdc, hdcMem;
|
|
HBITMAP hbm,hbmOld;
|
|
BITMAP bm;
|
|
|
|
BmpId = GetDlgCtrlID(hwnd);
|
|
|
|
hdc = BeginPaint(hwnd,&ps);
|
|
if(hbm = LoadBitmap((HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),MAKEINTRESOURCE(BmpId))) {
|
|
GetObject(hbm, sizeof(bm),&bm);
|
|
if(hdcMem = CreateCompatibleDC(hdc)) {
|
|
if(hbmOld = SelectObject(hdcMem,hbm)) {
|
|
BitBlt(hdc,0,0,bm.bmWidth,bm.bmHeight,hdcMem,0,0,SRCCOPY);
|
|
SelectObject(hdcMem,hbmOld);
|
|
}
|
|
DeleteDC(hdcMem);
|
|
}
|
|
DeleteObject(hbm);
|
|
}
|
|
EndPaint(hwnd,&ps);
|
|
}
|
|
|
|
|
|
LRESULT
|
|
BmpClassWndProc(
|
|
IN HWND hwnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
switch(msg) {
|
|
|
|
case WM_NCCREATE:
|
|
|
|
SetWindowLong(
|
|
hwnd,
|
|
GWL_STYLE,
|
|
GetWindowLong(hwnd,GWL_STYLE) | WS_BORDER
|
|
);
|
|
|
|
return(TRUE);
|
|
|
|
case WM_PAINT:
|
|
|
|
BmpClassPaint(hwnd);
|
|
return(0);
|
|
}
|
|
|
|
return(DefWindowProc(hwnd,msg,wParam,lParam));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////
|
|
//
|
|
// Action item list control
|
|
//
|
|
////////////////////////////////////////////
|
|
|
|
//
|
|
// Define locations in extra window storage
|
|
//
|
|
#define AIL_FONT (0)
|
|
#define AIL_BOLDFONT (sizeof(LONG))
|
|
#define AIL_BOLDITEM (2*sizeof(LONG))
|
|
#define AIL_TEXT (3*sizeof(LONG))
|
|
#define AIL_LINECOUNT (4*sizeof(LONG))
|
|
#define AIL_FREEFONTS (5*sizeof(LONG))
|
|
|
|
#define AIL_EXTRA 6
|
|
|
|
PCWSTR szActionItemListClassName = L"$$$ActionItemList";
|
|
|
|
|
|
VOID
|
|
ActionItemListPaint(
|
|
IN HWND hwnd
|
|
)
|
|
{
|
|
|
|
PAINTSTRUCT PaintStruct;
|
|
PWSTR p,Text;
|
|
UINT LineCount;
|
|
HFONT OldFont,Font,BoldFont;
|
|
UINT HighlightedItem;
|
|
UINT i;
|
|
int Length;
|
|
int y;
|
|
int yDelta;
|
|
HBITMAP Bitmap,OldBitmap;
|
|
BITMAP bitmap;
|
|
HDC MemoryDC;
|
|
SIZE Size;
|
|
RECT rect;
|
|
int Spacing;
|
|
#define BORDER 3
|
|
|
|
if(!BeginPaint(hwnd,&PaintStruct)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If no text, nothing to do.
|
|
//
|
|
if(Text = (PWSTR)GetWindowLong(hwnd,AIL_TEXT)) {
|
|
LineCount = (UINT)GetWindowLong(hwnd,AIL_LINECOUNT);
|
|
}
|
|
|
|
if(!Text || !LineCount) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get value indicating which item is to be bolded.
|
|
//
|
|
HighlightedItem = (UINT)GetWindowLong(hwnd,AIL_BOLDITEM);
|
|
|
|
//
|
|
// Get font handles.
|
|
//
|
|
Font = (HFONT)GetWindowLong(hwnd,AIL_FONT);
|
|
BoldFont = (HFONT)GetWindowLong(hwnd,AIL_BOLDFONT);
|
|
|
|
//
|
|
// Select the non-boldface font to get the handle of
|
|
// the currently selected font.
|
|
//
|
|
OldFont = SelectObject(PaintStruct.hdc,Font);
|
|
|
|
//
|
|
// Set text background color.
|
|
//
|
|
SetBkColor(PaintStruct.hdc,GetSysColor(COLOR_3DFACE));
|
|
|
|
//
|
|
// Load the little triangle bitmap and create a compatible DC for it.
|
|
//
|
|
Bitmap = LoadBitmap(NULL,MAKEINTRESOURCE(OBM_MNARROW));
|
|
|
|
if(MemoryDC = CreateCompatibleDC(PaintStruct.hdc)) {
|
|
|
|
OldBitmap = SelectObject(MemoryDC,Bitmap);
|
|
GetObject(Bitmap,sizeof(BITMAP),&bitmap);
|
|
}
|
|
|
|
Spacing = GetSystemMetrics(SM_CXICON) / 2;
|
|
|
|
//
|
|
// Treat the text as a series of lines and draw each one.
|
|
//
|
|
p = Text;
|
|
y = 0;
|
|
for(i=0; i<LineCount; i++) {
|
|
|
|
//
|
|
// Calculate the line's height based on the boldface font.
|
|
// This is used to get to the y coord of the next line.
|
|
//
|
|
SelectObject(PaintStruct.hdc,BoldFont);
|
|
|
|
GetClientRect(hwnd,&rect);
|
|
rect.left = (2 * BORDER) + Spacing;
|
|
rect.bottom = 0;
|
|
|
|
DrawText(PaintStruct.hdc,p,lstrlen(p),&rect,DT_CALCRECT|DT_WORDBREAK);
|
|
|
|
yDelta = rect.bottom + (2*BORDER);
|
|
|
|
//
|
|
// Change font to non-boldface for this line if necessary.
|
|
//
|
|
if(i != HighlightedItem) {
|
|
SelectObject(PaintStruct.hdc,Font);
|
|
}
|
|
|
|
rect.top = y + BORDER;
|
|
rect.left = (2 * BORDER) + Spacing;
|
|
rect.bottom = rect.top + yDelta;
|
|
|
|
//
|
|
// Draw the line's text.
|
|
//
|
|
Length = lstrlen(p);
|
|
DrawText(PaintStruct.hdc,p,Length,&rect,DT_WORDBREAK);
|
|
|
|
//
|
|
// Draw the little triangle thing if necessary.
|
|
//
|
|
if((i == HighlightedItem) && Bitmap && MemoryDC) {
|
|
|
|
GetTextExtentPoint(PaintStruct.hdc,L"WWWWW",5,&Size);
|
|
|
|
BitBlt(
|
|
PaintStruct.hdc,
|
|
BORDER,
|
|
y + ((Size.cy - bitmap.bmHeight) / 2) + BORDER,
|
|
bitmap.bmWidth,
|
|
bitmap.bmHeight,
|
|
MemoryDC,
|
|
0,0,
|
|
0x220326 // (NOT src) AND dest [DSna]
|
|
);
|
|
}
|
|
|
|
//
|
|
// Point to next line's text.
|
|
//
|
|
p += Length + 1;
|
|
y += yDelta;
|
|
}
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
if(OldFont) {
|
|
SelectObject(PaintStruct.hdc,OldFont);
|
|
}
|
|
|
|
if(MemoryDC) {
|
|
if(OldBitmap) {
|
|
SelectObject(MemoryDC,OldBitmap);
|
|
}
|
|
if(Bitmap) {
|
|
DeleteObject(Bitmap);
|
|
}
|
|
DeleteDC(MemoryDC);
|
|
}
|
|
|
|
EndPaint(hwnd,&PaintStruct);
|
|
}
|
|
|
|
|
|
LRESULT
|
|
ActionItemListWndProc(
|
|
IN HWND hwnd,
|
|
IN UINT msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
LRESULT rc;
|
|
HFONT OldFont,Font,BoldFont;
|
|
LOGFONT LogFont;
|
|
PWSTR Text;
|
|
PWSTR p;
|
|
UINT LineCount;
|
|
BOOL FreeFont,FreeBoldFont;
|
|
|
|
switch(msg) {
|
|
|
|
case WM_CREATE:
|
|
|
|
//
|
|
// Create fonts.
|
|
//
|
|
OldFont = (HFONT)SendMessage(GetParent(hwnd),WM_GETFONT,0,0);
|
|
if(!OldFont) {
|
|
//
|
|
// Using system font.
|
|
//
|
|
OldFont = GetStockObject(DEFAULT_GUI_FONT);
|
|
}
|
|
|
|
FreeFont = TRUE;
|
|
FreeBoldFont = TRUE;
|
|
if(OldFont && GetObject(OldFont,sizeof(LOGFONT),&LogFont)) {
|
|
|
|
LogFont.lfWeight = 400;
|
|
Font = CreateFontIndirect(&LogFont);
|
|
if(!Font) {
|
|
Font = GetStockObject(DEFAULT_GUI_FONT);
|
|
FreeFont = FALSE;
|
|
}
|
|
|
|
LogFont.lfWeight = 700;
|
|
BoldFont = CreateFontIndirect(&LogFont);
|
|
if(!BoldFont) {
|
|
BoldFont = Font;
|
|
FreeBoldFont = FALSE;
|
|
}
|
|
}
|
|
|
|
SetWindowLong(hwnd,AIL_FONT,(LONG)Font);
|
|
SetWindowLong(hwnd,AIL_BOLDFONT,(LONG)BoldFont);
|
|
SetWindowLong(hwnd,AIL_BOLDITEM,0);
|
|
SetWindowLong(hwnd,AIL_TEXT,0);
|
|
SetWindowLong(hwnd,AIL_LINECOUNT,0);
|
|
SetWindowLong(hwnd,AIL_FREEFONTS,MAKELONG(FreeFont,FreeBoldFont));
|
|
|
|
rc = 0;
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
//
|
|
// Get rid of fonts we created if necessary.
|
|
//
|
|
FreeFont = (BOOL)GetWindowLong(hwnd,AIL_FREEFONTS);
|
|
FreeBoldFont = HIWORD(FreeFont);
|
|
FreeFont = LOWORD(FreeFont);
|
|
|
|
if(FreeFont && (Font = (HFONT)GetWindowLong(hwnd,AIL_FONT))) {
|
|
DeleteObject(Font);
|
|
}
|
|
|
|
if(FreeBoldFont && (BoldFont = (HFONT)GetWindowLong(hwnd,AIL_BOLDFONT))) {
|
|
DeleteObject(BoldFont);
|
|
}
|
|
|
|
if(Text = (PWSTR)GetWindowLong(hwnd,AIL_TEXT)) {
|
|
MyFree(Text);
|
|
}
|
|
rc = 0;
|
|
break;
|
|
|
|
case WM_SETTEXT:
|
|
//
|
|
// Free old text and remember new text.
|
|
//
|
|
if(Text = (PWSTR)GetWindowLong(hwnd,AIL_TEXT)) {
|
|
MyFree(Text);
|
|
}
|
|
|
|
LineCount = 0;
|
|
if(Text = DuplicateString((PVOID)lParam)) {
|
|
//
|
|
// Count lines in the text. This is equal to the number of
|
|
// newlines. We require that the last line have a newline
|
|
// to be counted.
|
|
//
|
|
for(LineCount=0,p=Text; *p; p++) {
|
|
|
|
if(*p == L'\r') {
|
|
*p = L' ';
|
|
} else {
|
|
if(*p == L'\n') {
|
|
*p = 0;
|
|
LineCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cheat a little: we expect wParam to be the 0-based index
|
|
// of the boldfaced line. Callers will have to use SendMessage
|
|
// instead of SetWindowText().
|
|
//
|
|
SetWindowLong(hwnd,AIL_BOLDITEM,(LONG)wParam);
|
|
SetWindowLong(hwnd,AIL_LINECOUNT,LineCount);
|
|
SetWindowLong(hwnd,AIL_TEXT,(LONG)Text);
|
|
|
|
rc = (Text != NULL);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
|
|
ActionItemListPaint(hwnd);
|
|
rc = 0;
|
|
break;
|
|
|
|
default:
|
|
rc = DefWindowProc(hwnd,msg,wParam,lParam);
|
|
break;
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
BOOL
|
|
RegisterActionItemListControl(
|
|
IN BOOL Init
|
|
)
|
|
{
|
|
WNDCLASS wc;
|
|
BOOL b;
|
|
static BOOL Registered;
|
|
|
|
if(Init) {
|
|
if(Registered) {
|
|
b = TRUE;
|
|
} else {
|
|
wc.style = CS_PARENTDC;
|
|
wc.lpfnWndProc = ActionItemListWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = AIL_EXTRA * sizeof(LONG);
|
|
wc.hInstance = MyModuleHandle;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = szActionItemListClassName;
|
|
|
|
if(b = (RegisterClass(&wc) != 0)) {
|
|
Registered = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
if(Registered) {
|
|
if(b = UnregisterClass(szActionItemListClassName,MyModuleHandle)) {
|
|
Registered = FALSE;
|
|
}
|
|
} else {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
|
|
return(b);
|
|
}
|