One of the things that is interesting to me as a student of the Chinese languages is to recognize where the traditional and simplified Chinese scripts differ. Since this is my program, I'm going to hard-code the color for simplified Chinese script: maroon.

To accomplish the highlighting, we take advantage of listview's custom-draw feature. Custom-draw allows you to make minor changes to the way items are displayed on the screen. It's a middle ground between having listview do all the work (via default drawing behavior) and having the program do all the work (via owner-draw).

The custom-draw cycle for shell common controls consists of series of NM_CUSTOMDRAW notifications, starting with the most general and getting more specific. The reason for the break-down is multi-fold. First, it allows the listview control to short-circuit a portion of custom-draw behavior if the parent window does not indicate that it wishes to customize a particular behavior. This reduces message traffic and improves performance when large numbers of items are being drawn. Second, it allows the parent window to target its customizations to the drawing stages it is interested in.

Listviews are peculiar among the shell common controls in that its items sometimes (but not always) have sub-items. This complicates the drawing process since it requires listview to accomodate both styles: large icon view does not use sub-items, but report view does. To address this, the CDDS_ITEMPREPAINT stage is entered when an item is about to paint, and any changes made by the parent window are considered to be effective for the entire item. If you want to make changes on a per-subitem basis, you have to respond to CDDS_ITEMPREPAINT | CDDS_SUBITEM and set your properties (or reset them if you want to return to the default) for that sub-item.

With those preliminary remarks settled, we can dive in.

class RootWindow : public Window
{
 ...
protected:
 ...
 LRESULT OnLVCustomDraw(NMLVCUSTOMDRAW* pcd);
 ...
private:
 HWND m_hwndLV;
 COLORREF m_clrTextNormal;
 Dictionary m_dict;
};

We declare our listview custom-draw handler as well as the member variable in which we remember the normal text color so that we can reset it for columns we do not intend to colorize.

LRESULT RootWindow::OnNotify(NMHDR *pnm)
{
 switch (pnm->code) {
 case LVN_GETDISPINFO:
  OnGetDispInfo(CONTAINING_RECORD(pnm, NMLVDISPINFO, hdr));
  break;
 case NM_CUSTOMDRAW:
  if (pnm->hwndFrom == m_hwndLV) {
   return OnLVCustomDraw(CONTAINING_RECORD(
                         CONTAINING_RECORD(pnm, NMCUSTOMDRAW, hdr),
                                                NMLVCUSTOMDRAW, nmcd));
  }
  break;
 }
 return 0;
}

If we receive a NM_CUSTOMDRAW notification from the listview control, we call our new handler. The multiple calls to the CONTAINING_RECORD macro are necessary because the NMHDR structure is nestled two levels deep inside the NMLVCUSTOMDRAW structure.

LRESULT RootWindow::OnLVCustomDraw(NMLVCUSTOMDRAW* pcd)
{
 switch (pcd->nmcd.dwDrawStage) {
 case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW;
 case CDDS_ITEMPREPAINT:
  m_clrTextNormal = pcd->clrText;
  return CDRF_NOTIFYSUBITEMDRAW;
 case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
  pcd->clrText = m_clrTextNormal;
  if (pcd->iSubItem == COL_SIMP &&
    pcd->nmcd.dwItemSpec < (DWORD)Length()) {
    const DictionaryEntry& de = Item(pcd->nmcd.dwItemSpec);
    if (de.m_pszSimp) {
      pcd->clrText = RGB(0x80, 0x00, 0x00);
    }
  }
  break;
 }
 return CDRF_DODEFAULT;
}

During the CDDS_PREPAINT stage, we indicate our desire to receive CDDS_ITEMPREPAINT notifications. During the CDDS_ITEMPREPAINT stage, we save the normal text color and indicate that we want to receive sub-item notifications. It is in the sub-item notification CDDS_ITEMPREPAINT | CDDS_SUBITEM that the real work happens.

First, we reset the color to the default on the assumption that we will not need to colorize this column. But if the column is the simplified Chinese column, if the item number is valid, and if the simplified Chinese is different from the traditional Chinese, then we set the text color to maroon.

That's enough with the Chinese/English dictionary for now. All this time, and we don't even have search capability yet! We'll work on that next month.