I am writing a plugin for Finale music program that uses a Rich Edit control to display and edit formatted text. One of the formats is a "hidden" style which really just means that it is grayed out. The "hidden"-ness only applies when Finale prints the text. It turns out CFM_DISABLED
/CFE_DISABLED
works exactly as I need for displaying a section of "hidden" text. It automatically grays out the text marked disabled, but otherwise it has no impact on the user's ability to edit the text.
Here is the code I use to do this:
static CHARFORMAT2W charFormatFromFontInfo(const FCFontInfo *pFont)
{
FCString fontName;
pFont->GetNameString(&fontName);
CHARFORMAT2W cf{};
cf.cbSize = sizeof(cf);
cf.dwMask = CFM_BOLD | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_STRIKEOUT | CFM_UNDERLINE | CFM_DISABLED;
lstrcpynW(cf.szFaceName, (LPCWSTR)fontName._GetUnicodeBuffer(), DIM(cf.szFaceName));
cf.szFaceName[DIM(cf.szFaceName) - 1] = 0;
cf.yHeight = 20.0 * pFont->GetSizeFloat(); // points to twps
if (pFont->GetBold())
cf.dwEffects |= CFE_BOLD;
if (pFont->GetItalic())
cf.dwEffects |= CFE_ITALIC;
if (pFont->GetUnderline())
cf.dwEffects |= CFE_UNDERLINE;
if (pFont->GetStrikeOut())
cf.dwEffects |= CFE_STRIKEOUT;
if (pFont->GetHidden())
cf.dwEffects |= CFE_DISABLED;
return cf;
}
The problem comes when I add color ranges to the control. When I add a color to a range of text, the control loses any visible representation of "disabled"-ness if a disabled passage overlaps the range where I change the color. That's okay for my purposes, but I would like to be able to reset the colors and regain the display of disabled sections as grayed-out. I can't figure out how to do this. I have tried, simply:
// after setting the selection to the entire text
CHARFORMAT2W cf{};
cf.cbSize = sizeof(cf);
cf.dwMask = CFM_COLOR;
cf.crTextColor = RGB(0, 0, 0);
SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETCHARFORMAT, SCF_SELECTION | SCF_DEFAULT, (LPARAM)&cf);
This restored the black text, but it also changed all the disabled text black as well.
ChatGPT suggested setting the cf.crTextColor
member to -1, but that was a hallucination. (It didn't work.)
My question boils down to, is there a way to back out all the color changes and restore the default behavior where CFE_DISABLED
text is grayed out?
The answer is the CFE_AUTOCOLOR
flag. The Microsoft documents are not completely clear on this point, but it is the solution.
// after setting the selection to the entire text
CHARFORMAT2W cf{};
cf.cbSize = sizeof(cf);
cf.dwMask = CFM_COLOR;
cf.dwEffects = CFM_AUTOCOLOR;
cf.crTextColor = RGB(0, 0, 0); // may not strictly be necessary but is clean
SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETCHARFORMAT, SCF_SELECTION | SCF_DEFAULT, (LPARAM)&cf);
This causes the text to be colored with system defaults, which includes graying out text marked with CFM_DISABLED
.