text: #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);
  }
  