2
votes

I have a CRichEditCtrl in an MFC project, which I use as a report log. Depending on the given situation, I need to append different colored text to the control (ie. a blue line for standard notifications, a red line for errors, etc). I've come pretty close to getting this to work, but it still behaves strangely:

void CMyDlg::InsertText(CString text, COLORREF color, bool bold, bool italic)
{
    CHARFORMAT cf = {0};
    CString txt;
    int txtLen = m_txtLog.GetTextLength();
    m_txtLog.GetTextRange(0, txtLen, txt);

    cf.cbSize = sizeof(cf);
    cf.dwMask = (bold ? CFM_BOLD : 0) | (italic ? CFM_ITALIC : 0) | CFM_COLOR;
    cf.dwEffects = (bold ? CFE_BOLD : 0) | (italic ? CFE_ITALIC : 0) |~CFE_AUTOCOLOR;
    cf.crTextColor = color;

    m_txtLog.SetWindowText(txt + (txt.GetLength() > 0 ? "\n" : "") + text);
    m_txtLog.SetSel(txtLen, m_txtLog.GetTextLength());
    m_txtLog.SetSelectionCharFormat(cf);
}

At best, the end result is that the newly appended line is appropriately colored but all of the previous text turns black. On top of that, for each appended line of text, the starting selection seems to increase by 1. For example:

Call #1:
- [RED]This is the first line[/RED]

Call #2:
- [BLACK]This is the first line[/BLACK]
- [GREEN]This is the second line[/GREEN]

Call #3:
- [BLACK]This is the first line[/BLACK]
- [BLACK]This is the second line[/BLACK]
- [BLUE]This is the third line[/BLUE]

Call #4:
- [BLACK]This is the first line[/BLACK]
- [BLACK]This is the second line[/BLACK]
- [BLACK]This is the third line[/BLACK]
- [BLACK]T[/BLACK][YELLOW]his is the fourth line[/YELLOW]

Call #5:
- [BLACK]This is the first line[/BLACK]
- [BLACK]This is the second line[/BLACK]
- [BLACK]This is the third line[/BLACK]
- [BLACK]This is the fourth line[/BLACK]
- [BLACK]Th[/BLACK][ORANGE]is is the fifth line[/ORANGE]

etc...

So how can I fix this to where all the previous text and formatting remain as-is, while appending a new line of colored text?

3

3 Answers

6
votes

Your example code reads the old text out of the dialog with a call to GetTextRange(). This does not include any rich formatting so, when the text is put back in place, it is not formatted. You can completely forgo this by "inserting" at the end of the text area by setting the cursor to the end without any selection and calling ReplaceSel().

I think your method should look something like this:

void CMFCApplication2Dlg::InsertText(CString text, COLORREF color, bool bold, bool italic)
{
    CHARFORMAT cf = {0};
    int txtLen = m_txtLog.GetTextLength();

    cf.cbSize = sizeof(cf);
    cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_COLOR;
    cf.dwEffects = (bold ? CFE_BOLD : 0) | (italic ? CFE_ITALIC : 0);
    cf.crTextColor = color;

    m_txtLog.SetSel(txtLen, -1); // Set the cursor to the end of the text area and deselect everything.
    m_txtLog.ReplaceSel(text); // Inserts when nothing is selected.

    // Apply formating to the just inserted text.
    m_txtLog.SetSel(txtLen, m_txtLog.GetTextLength());
    m_txtLog.SetSelectionCharFormat(cf);
}
0
votes

The thing I learned from trying to get this working is that you can't just set the flags like the examples above. Doing this:

 cf.dwMask = (bold ? CFM_BOLD : 0) | (italic ? CFM_ITALIC : 0) | CFM_COLOR;

Half works, but you will waste a lot of time if you are using the CRichEditCtrl (as an example) and getting the current formatting (though I think this was happening no matter what for me..) The issue is that the flags above are setting what you want to toggle the value of. So, this is actually more valid:

CHARFORMAT cf;
cf.cbSize = sizeof(CHARFORMAT);
GetSelectionCharFormat(cf);

cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_COLOR; //set these to specify the flags you are changing

if (bold)
{
    cf.dwEffects |= CFE_BOLD; //toggle on
}
else
{
    cf.dwEffects &= ~CFE_BOLD; //toggle off
}

if (italic)
{
    cf.dwEffects |= CFE_ITALIC; //toggle on
}
else
{
    cf.dwEffects &= ~CFE_ITALIC; //toggle off
}

cf.dwEffects &= ~CFE_AUTOCOLOR; //this only seemed to work if I set it this way

If you don't do it that way, the formatting will remain - even though you cleared it. It made me scratch my head for a while, and I did a lot of googling to fix it.

0
votes

Assign the format prior to the insertion of the new text. If you apply the format after insertion, you will end up with an incorrectly formatted text.

CHARFORMAT cf = { 0 };
int txtLen =  GetTextLength();

cf.cbSize = sizeof(cf);
cf.dwMask = CFM_COLOR;
cf.dwEffects = ~CFE_AUTOCOLOR;//No auto color
cf.crTextColor = RGB(0, 0, 255);//color of the text

SetSel(txtLen, -1);//Deselect all
SetSelectionCharFormat(cf);//Assign the format to the cursor pos
ReplaceSel(newText);