tags:

views:

340

answers:

4

It has methods like CRichEditCtrl::Copy(), CRichEditCtrl::Paste() which you can call, but I can't spot any messages the control is sent by Windows telling it to perform a paste operation. Does anyone know if such a thing exists? Or does CRichEditCtrl do something lower-level like monitoring WM_CHAR events? If so can I reuse any internal methods or would I just have to roll my own in order to override the standard paste functionality?

What I actually want is for my custom subclass (CMyRichEditCtrl : CRichEditCtrl) to ignore any formatting on text pasted in to the control. Either by getting the clipboard data in a different clipboard format, or by pasting it in as normal and immediately removing formatting on inserted text.

What I tried so far:

  1. Checking the message for WM_PASTE in CMyRichEditCtrl::PreTranslateMessage()
  2. Creating a method virtual void CMyRichEditCtrl::Paste()
  3. Putting a breakpoint on CRichEditCtrl::Paste() in afxcmn.inl
  4. Dumping every message passing through CMyRichEditCtrl::PreTranslateMessage()

Results:

1: No WM_PASTE message seen
2: It's never called
3: It's never hit... how?
4: The control never receives any WM_COMMAND, WM_PASTE or focus-related messages. Basically only mouse-move and key-press messages.

It seems other people have actually done this successfully. I'm wondering if my MFC version or something could be screwing it up, at this point.

A: 

Windows defines messages for cut/copy/and paste. see WM_CUT.

It's probably responding to those messages rather than to WM_CHAR messages to know when to do clipboard operations.

John Knoeller
Not as far as I can tell: http://www.codeguru.com/forum/showthread.php?t=58725
John
Not clear from that link, is that supposedly a subclass of the richedit control's window proc?
John Knoeller
See my edits to the original post, should make it clearer what I'm trying.
John
A: 

What happens when the user requests a paste action is usually that a WM_COMMAND message with the identifier ID_EDIT_PASTE is sent to the rich edit control. By default in MFC this is handled by CRichEditCtrl::OnEditPaste(), which calls Paste() on the edit control itself.

The way I'd go about this is to derive a class from CRichEditCtrl, add an OnEditPaste method and route the message to it with a

ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)

declaration, which should work. Alternatively, in your PreTranslateMessage you could look for WM_COMMAND with a wParam of ID_EDIT_PASTE.

By the way, I've solved a very similar problem to yours (paste without formatting) by just having an implementation of OnEditPaste with

void MyRichEdit::OnEditPaste()
{
  SendMessage(EM_PASTESPECIAL,CF_UNICODETEXT);
}

This responds to the paste request by sending a paste message to the control that insists that the data format is plain text.

Finally, I should point out that the above technique is sufficient to catch all pastes triggered from the user interface. However, it won't catch programmatically triggered pastes, when your code sends WM_PASTE to the edit control. In those cases it's easiest to just change your code. However, if you really want to intercept such cases, you have to get your hands dirty with COM and IRichEditOleCallback::QueryAcceptData. But you almost certainly don't want to go there :-)

DavidK
I've tried this (both ON_COMMAND and scanning for WM_COMMAND messages. Neither is triggered when I hit CTRL-V, the text just appears in the edit box and EN_CHANGE is triggered. I'm totally confused now - how did you get it working but it refuses to for me?!
John
Interesting! It might be worth trying the IRichEditOleCallback::QueryAcceptData approach, if only to see what's going on.Try creating a class implementing IRichEditOleCallback, and install it by calling CRichEditCtrl::SetOLECallback(). Now on a paste the class' QueryAcceptData should always be called no matter what. With a breakpoint on that you should at least get a callstack which might have a clue as to what message it's responding to.
DavidK
+1  A: 

Handle EN_PROTECTED message.

ON_NOTIFY_REFLECT(EN_PROTECTED, &YourClass::OnProtected)

// call this from the parent class
void YourClass::Initialize()
{
    CHARFORMAT format = { sizeof(CHARFORMAT) };
    format.dwEffects = CFE_PROTECTED; 
    format.dwMask = CFM_PROTECTED;

    SetDefaultCharFormat(format);
    SetEventMask(ENM_PROTECTED);
}

void YourClass::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
    *pResult = 0; 

    ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
    if (pEP->msg == WM_PASTE)
        pResult = 1; // prevent paste
}
Nikola Smiljanić
Can you explain why SetDefaultCharFormat makes it work (it does)?I say it works, but this approach only _tells_ me the operation has happened, it doesn't let me override it. I'll still give you the bounty _if_ nothing better is proposed since even the part you answer is very helpful.
John
Or immediately of course if _you_ can tell me how OnProtected method can prevent/modify the action that happens to protected chars.
John
Yes I spotted that too! But you certainly deserve it, I've been asking the same question in different ways for a week without success!
John
Documentation says:This message returns zero to allow the operation.This message returns a nonzero value to prevent the operation.
Nikola Smiljanić
A: 

Use the ON_MESSAGE Macro on your derived class.

ON_MESSAGE(WM_PASTE, OnPaste)

LRESULT CMyRichEditCtrl::OnPaste(WPARAM, LPARAM)

If you open the RichEdit.h file, you will notice that some of the messages are on the range of WM_USER. Maybe this is how MFC handles the events for the Rich Edit Control.

This simply doesn't work in any tests I perform. Have you tested it or did you just notice WM_PASTE exists and assume it would work?
John
I think it depends on how you use it. I assume that you have a window that utilizes this RichEditCtrl derived class you have.In my case, I have a dialog that call the Paste() function of the derived class and in return triggers the code I posted above.