Search code examples
c++winapirichedit

Reset Rich Edit control to default colors


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?


Solution

  • 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.