CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Feb 2009
    Location
    Portland, OR
    Posts
    1,488

    Apply formatting to Richedit control on the fly

    I'm trying to apply color formatting to the text in the richedit control pretty much like FrontPage used to do for the HTML text, but I don't really know how to do it.

    I believe that it has to be done in a separate thread while analyzing text being typed into the control.

    I tried looking at the ways to apply the coloring to the richedit text and so far I was able to find the SetSelectionCharFormat() member function that can do it, but my problem is that if I start selecting text from the background thread it will interfere with the a selection and typing.

    Does anyone know how to do it?

  2. #2
    Join Date
    Feb 2005
    Posts
    2,160

    Re: Apply formatting to Richedit control on the fly

    You need to handle the EN_CHANGED notification in your rich-edit control and apply the formatting each time the text is changed.

  3. #3
    Join Date
    Feb 2009
    Location
    Portland, OR
    Posts
    1,488

    Re: Apply formatting to Richedit control on the fly

    Yeah, that's what I thought. But how do you change the color of the text?

  4. #4
    Join Date
    Feb 2005
    Posts
    2,160

    Re: Apply formatting to Richedit control on the fly

    Basically, each time you format the line of text (with every character typed when using EN_CHANGE), you save the current cursor position with a call to GetSel(), find the character positions of the places you want to change (probably in a loop to get multiple words, etc), use SetSel() to select the text between those positions, call SetSelectionCharFormat() to change the text, then return to your saved coordinates to await the next character typed by the user. You should begin your routine by calling SetRedraw(0) and end it with SetRedraw(1) so that none of the selection and format changes actually draw to the control until it is all finished. This avoids flicker and other artifacts.

    Keep in mind that all this moving, selecting and formatting behind the scenes really messes up the undo buffer but there's a way around that if you're interested. It requires grabbing a pointer to the IRichEditOle interface and querying it for the ITextDocumnet interface, then calling ITextDocument's Undo() with tomSuspend and tomResume.

  5. #5
    Join Date
    Feb 2009
    Location
    Portland, OR
    Posts
    1,488

    Re: Apply formatting to Richedit control on the fly

    Quote Originally Posted by hoxsiew View Post
    Basically, each time you format the line of text (with every character typed when using EN_CHANGE), you save the current cursor position with a call to GetSel(), find the character positions of the places you want to change (probably in a loop to get multiple words, etc), use SetSel() to select the text between those positions, call SetSelectionCharFormat() to change the text, then return to your saved coordinates to await the next character typed by the user. You should begin your routine by calling SetRedraw(0) and end it with SetRedraw(1) so that none of the selection and format changes actually draw to the control until it is all finished. This avoids flicker and other artifacts.

    Keep in mind that all this moving, selecting and formatting behind the scenes really messes up the undo buffer but there's a way around that if you're interested. It requires grabbing a pointer to the IRichEditOle interface and querying it for the ITextDocumnet interface, then calling ITextDocument's Undo() with tomSuspend and tomResume.
    Wow, thanks. That's really an indepth stuff. Do you think there's a sample code somewhere to illustrate this approach (especially with the interfaces and undo buffer)?

  6. #6
    Join Date
    Feb 2005
    Posts
    2,160

    Re: Apply formatting to Richedit control on the fly

    I've pretty much stumbled my way through all this while writing a custom script editor; unfortunately, it's company confidential so I can't just post it.

    The interface stuff is pretty straight forward:

    Code:
    #include <tom.h>
    #include <Richole.h>
    
    class CMyRichEditCtrl : public CRichEditCtrl
    {
      //...
      IRichEditOle* m_iCtrl;
      ITextDocument *m_iDoc;
      virtual void PreSubclassWindow();
      void FormatLine(void);
      DECLARE_MESSAGE_MAP()
      afx_msg void OnEnChange();
      //...
    };
    
    
    BEGIN_MESSAGE_MAP(CMyRichEditCtrl, CRichEditCtrl)
      ON_CONTROL_REFLECT(EN_CHANGE, OnEnChange) 
    END_MESSAGE_MAP()
    
    
    
    void CMyRichEditCtrl::PreSubclassWindow()
    {
      //...
      m_iCtrl=GetIRichEditOle();
      if(m_iCtrl){
        HRESULT hr=m_iCtrl->QueryInterface(__uuidof(ITextDocument),(void **)&m_iDoc);
        if(hr!=S_OK){
          m_iDoc=NULL;
        }
      }
      CRichEditCtrl::PreSubclassWindow();
    }
    
    void CMyRichEditCtrl::FormatLine()
    {
      long a,b;
    
      GetSel(a,b);  //save current position
      SetRedraw(0);
      m_iDoc->Undo(tomSuspend,NULL);  //suspend undo
    
      //... Do your formatting
      
      SetSel(a,b);  //restore position
      SetRedraw(1);
      m_iDoc->Undo(tomResume,NULL);  //resume normal undo/redo
      
    
    }
    
    void CRichSyntaxCtrl::OnEnChange()
    {
      //...
      FormatLine();
      //...
    }

  7. #7
    Join Date
    Feb 2009
    Location
    Portland, OR
    Posts
    1,488

    Re: Apply formatting to Richedit control on the fly

    Hey, thanks again. I'd give you some reputation but this thing tells me I can't now. I also understand about the copyright issues, so no need to share the source code.

    From your experience, do you think if it's better to do all the coloring/formatting in a separate background thread, or after EN_CHANGED notification?

  8. #8
    Join Date
    Feb 2005
    Posts
    2,160

    Re: Apply formatting to Richedit control on the fly

    In my experience, no thread is needed. Updates done during the course of the EN_CHANGED message handler are fast enough that the user sees no delay during regular entry into the control, but we have a limited set of keywords, etc. that need to be parsed and no formatting that needs to extend past a linefeed (like multiline comments in C) so we only update the line on which the user is currently typing.

    My advise would be to try it and see. Do some profiling, etc. If your needs dictate a different approach, then work it out.

  9. #9
    Join Date
    Feb 2009
    Location
    Portland, OR
    Posts
    1,488

    Re: Apply formatting to Richedit control on the fly

    Thanks. Unfortunately in my case I can't limit my control with that. The specification for it to be multi-line and fully scrollable both horizontally and vertically. The size of the text is not limited to a small file either.

    I tried browsing through the CodeGuru/Project articles and I couldn't find any that would provide this functionality for the RichEdit control. There were many that dealt with a user-created controls but not RichEdit. There are two that used it like that but I could clearly see in the comments section that those controls were too slow to process long text. I think I will try to code one myself and maybe post an article there if I succeed.

    Regarding your suggestion about processing the EN_CHANGED message, how do you know which part of the text was actually changed?

  10. #10
    Join Date
    Feb 2005
    Posts
    2,160

    Re: Apply formatting to Richedit control on the fly

    Quote Originally Posted by ahmd View Post
    Thanks. Unfortunately in my case I can't limit my control with that. The specification for it to be multi-line and fully scrollable both horizontally and vertically. The size of the text is not limited to a small file either.

    I tried browsing through the CodeGuru/Project articles and I couldn't find any that would provide this functionality for the RichEdit control. There were many that dealt with a user-created controls but not RichEdit. There are two that used it like that but I could clearly see in the comments section that those controls were too slow to process long text. I think I will try to code one myself and maybe post an article there if I succeed.

    Regarding your suggestion about processing the EN_CHANGED message, how do you know which part of the text was actually changed?
    You can certainly get clever with your algorithm; that's what programmers do. I've found that you can do a ****load of processing in the time between user keystrokes if you keep optimization in mind.

    Scrolling isn't much of an issue. You can format everything at the start, then only worry about the stuff that needs to be changed dynamically in your EN_CHANGED handler. Even if you have to scan a few lines back or forward looking for tokens, this is still really fast compared to the time it takes a user to type a key.

    As for what changes at EN_CHANGED, in my case, it doesn't matter. Only that something has changed is significant. Just format everything on the line that was changed. In your case, you may want to scan everything that is visible in the current viewport and reformat, or just look for tokens and change only things relevant between the tokens. If the change could affect stuff outside the viewport, maybe spawn or signal a thread to process in the background, but this may require synchronization and such between keystrokes and adds a lot of complexity when it may not be needed.

    I really think you'll be surprised at how much you can get done in real-time in a decently constructed handler.

  11. #11
    Join Date
    Jun 2009
    Posts
    2

    Re: Apply formatting to Richedit control on the fly

    Hello hoxsiew, I was thrilled to see your post regarding how to suspend the undo buffer...I already had a program setup exactly like the structure you show, but I wasn't aware of accessing the oleobject or the itextdocument...

    Part of what you posted was:
    void CMyRichEditCtrl::FormatLine()
    {
    long a,b;

    GetSel(a,b); //save current position
    SetRedraw(0);
    m_iDoc->Undo(tomSuspend,NULL); //suspend undo

    //... Do your formatting

    SetSel(a,b); //restore position
    SetRedraw(1);
    m_iDoc->Undo(tomResume,NULL); //resume normal undo/redo


    }


    For me, trying to disable the redrawing and then re-enable it just causes my rich edit box to freeze and you can't do anything to do it.
    Anyway, my question is regarding invoking the Undo method of the itextdocument...my ->Undo(tomSuspend, NULL); always fails, returns false. But the ->Undo(tomResume, NULL) works fine. But of course, I can't achieve what I wanted to since all it's doing is resuming every time...

    Everything else works fine and I've verified that the rest of your code is working for me...getting the oleobject and then querying the interface, etc...I'm checking all that and using ASSERTS to be sure the pointer is valid, etc.

    Curiously enough, in the msft documentation, it says if you use tomFalse it will suspend the buffer and then tomTrue will resume it. If I use tomFalse and tomTrue (instead of tomSuspend and tomResume) to try and suspend/resume the Undo, the methods do NOT fail, but it is still not working because for example, when I paste say 5 lines of code and then do an undo, all that happens is the cursor jumps back up one line...if you keep undoing the cursor will back up each line and eventually the block of text you just pasted will get removed correctly. This, however, is not consistent, and sometimes if you do something and then try to undo it, you just keep undoing all day and nothing every happens.

    I have all of my line coloring stuff being done after the "suspend" and before the resume.
    As a side note, I've also tried to use the BeginCollection() right before I do a copy and then EndCollection() right after the copy, in the hopes that it had something to do with they way the richedit actually does its Copy() operation, but this too does not work--the BeginCollection() fails for some reason...the endcollection() though will return true.

    Do you or anyone else have any idea why the attempt at suspending the buffer would fail? I would greatly appreciate any thoughts or ideas as to what might be going on.

    Thanks!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured