 |
|
 |
If you have errors in compilation of source files just try to use these methods instead of original ones:
LRESULT CPopupDemoDlg::OnSelectAll(WPARAM wparam, LPARAM lparam) { m_RichEdit.SetSel(0,-1);
return (LRESULT)0; }
LRESULT CPopupDemoDlg::OnCopy(WPARAM wparam, LPARAM lparam) { m_RichEdit.Copy(); return (LRESULT)0; }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I tested your sample project, when I ran the release version, I clicked the right mouse button on the richedit when there was no input in it, only after clicking on the "select all" or "copy" for 2 times, the release EXE aborted with error.
I did so in the debug version, no such errors.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I created a richedit context menu in a dialog based program, the popup menu had only 1 menu item ID_MENU_TEST, I followed the PopupDemo.exe, after I right clicked on the richedit, the popup menu showed, when clicked the menu item "Test", I got nothing, looked like the message of clicking on the "Test" wasn't sent to the dialog, I looked into the code again and again, and found the difference. In my program''s message map, it was ON_COMMAND(ID_MENU_TEST, OnTest) , created by classwizard, by selecting object ID ID_MENU_TEST and the message COMMAND. But it didn't work.
after I manualy changed it into ON_MESSAGE(ID_MENU_TEST, OnTest), the menu worked.
I don't understand why the macro of ON_COMMAND created with classwizard didn't work, Thanks for help
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
I came up with the following solution. Override the virtual function GetContextMenu() within your CRichEditView derived class and add the following code.
CRichEditCtrl& Edit = GetRichEditCtrl();
// load menu items from User32.dll for system dependant language CString strMenu[] = { "&Undo", "Cu&t", "&Copy", "&Paste", "&Delete", "Select &All" }; int nCmd[] = {WM_UNDO, WM_CUT, WM_COPY, WM_PASTE, WM_CLEAR, EM_SETSEL}; CMenu menu; HMODULE hUser32 = LoadLibrary("USER32"); if (hUser32) { if (menu.Attach(LoadMenu(hUser32, MAKEINTRESOURCE(1)))) { for (int i = _countof(nCmd); VERIFY(menu.GetMenuString(nCmd[i], strMenu[i], MF_BYCOMMAND) != 0); } menu.Detach(); FreeLibrary(hUser32); }
// create the context menu menu.CreatePopupMenu(); menu.AppendMenu(MF_STRING, ID_EDIT_UNDO, strMenu[0]); menu.AppendMenu(MF_SEPARATOR); menu.AppendMenu(MF_STRING, ID_EDIT_CUT, strMenu[1]); menu.AppendMenu(MF_STRING, ID_EDIT_COPY, strMenu[2]); menu.AppendMenu(MF_STRING, ID_EDIT_PASTE, strMenu[3]); menu.AppendMenu(MF_STRING, ID_EDIT_CLEAR, strMenu[4]); menu.AppendMenu(MF_SEPARATOR); menu.AppendMenu(MF_STRING, ID_EDIT_SELECT_ALL, strMenu[5]);
// call the update handlers CallPopupMenuUpdateHandlers(&menu, this);
HMENU hMenu = menu.Detach(); return hMenu;
As you might have noticed, MFC does not automatically call the required update handlers for the menu items. Although MFC tries to do a lot of work to make programming as easy as possible, I've encountered several situations where MFC did not the perform the automatic magic for the update handlers. In these situations I manually added the function CallPopupMenuUpdateHandlers(). The function was copied from a part of the CFrameWnd class, because the functionality is not being exposed in another way.
The implementation for CallPopupMenuUpdateHandlers() looks like:
void CallPopupMenuUpdateHandlers(CMenu* pMenu, CCmdTarget* pTarget) { CCmdUI state; state.m_pMenu = pMenu; ASSERT(state.m_pOther == NULL); ASSERT(state.m_pParentMenu == NULL);
state.m_nIndexMax = pMenu->GetMenuItemCount(); for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) { state.m_nID = pMenu->GetMenuItemID(state.m_nIndex); if (state.m_nID == 0) continue; ASSERT(state.m_pOther == NULL); ASSERT(state.m_pMenu != NULL); if (state.m_nID == (UINT)-1) { state.m_pSubMenu = pMenu->GetSubMenu(state.m_nIndex); if (state.m_pSubMenu == NULL || (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 || state.m_nID == (UINT)-1) { continue; } state.DoUpdate(pTarget, FALSE); } else { state.m_pSubMenu = NULL; state.DoUpdate(pTarget, true); }
UINT nCount = pMenu->GetMenuItemCount(); if (nCount < state.m_nIndexMax) { state.m_nIndex -= (state.m_nIndexMax - nCount); while (state.m_nIndex < nCount && pMenu->GetMenuItemID(state.m_nIndex) == state.m_nID) { state.m_nIndex++; } } state.m_nIndexMax = nCount; } }
Hope this code is helpfull to some of you! Marc
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
sorry, the code snippets are not completely in correct style. I don't know why? but anyway... it might be of help for anyone. Marc
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
You can use User32.dll for have an international Context Menu
void RichEdit_ContextMenu(HWND hwndRichEdit){
BOOL bCandUndo=SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0); BOOL bCandRedo=SendMessage(hwndRichEdit, EM_CANREDO, 0, 0);
DWORD dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE); DWORD ichSelectionStart; DWORD ichSelectionEnd; SendMessage(hwndRichEdit, EM_GETSEL, OUT (WPARAM)&&RichSelectionStart, OUT (LPARAM)&&RichSelectionEnd); int cchSelection = ichSelectionEnd - ichSelectionStart;
BOOL bCanCut=(cchSelection && !(dwStyle&ES_READONLY)); BOOL bCanCopy=(cchSelection); BOOL bCanPaste=SendMessage(hwndRichEdit, EM_CANPASTE, 0, 0);
HMODULE hUser32=LoadLibrary("USER32");
char Strings[6][100]={"&Undo","Cu&t","&Copy","&Paste","&Delete","Select &All"}; int Ids[6]={WM_UNDO,WM_CUT,WM_COPY,WM_PASTE,WM_CLEAR,EM_SETSEL};
if (hUser32){ HMENU hMenu=LoadMenu(hUser32,(LPCSTR)1); for (int i=5;i>=0;--i) GetMenuItemString(hMenu,Ids[i],false,Strings[i],100); FreeLibrary(hUser32); }
HMENU hMenu = CreatePopupMenu(); AppendMenuEx(hMenu,MF_STRING|(bCandUndo?0:MF_DISABLED),Ids[0],Strings[0]); AppendMenu(hMenu,MF_SEPARATOR,0,0); AppendMenuEx(hMenu,MF_STRING|(bCanCut?0:MF_DISABLED),Ids[1],Strings[1]); AppendMenuEx(hMenu,MF_STRING|(bCanCopy?0:MF_DISABLED),Ids[2],Strings[2]); AppendMenuEx(hMenu,MF_STRING|(bCanPaste?0:MF_DISABLED),Ids[3],Strings[3]); AppendMenu(hMenu,MF_STRING,Ids[4],Strings[4]); AppendMenu(hMenu,MF_SEPARATOR,0,0); AppendMenu(hMenu,MF_STRING,Ids[5],Strings[5]);
POINT pt; GetCursorPos(OUT &pt);
int nCmdId = TrackPopupMenu(hMenu,TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,pt.x, pt.y,0, hwndRichEdit, NULL);
DestroyMenu(hMenu);
switch (nCmdId) { case EM_UNDO: case WM_CUT: case WM_COPY: case WM_PASTE: case WM_CLEAR: case EM_SETSEL: SendMessage(hwndRichEdit, nCmdId, 0, -1); break; } }
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
I added this code to my CRichEditCtrl and gave the menu items the same IDs as my tool bar buttons but they don't work. Also my Update Command UIs don't disable the menu when for example cut and copy are not valid menu items.
|
| Sign In·View Thread·PermaLink | 2.00/5 (2 votes) |
|
|
|
 |
|
 |
Thanks for this article - saved me a lot of research too.
I made one addition to it - you can detect whether a control is rich edit by finding out what its class is - and then adding that into the message loop automatically adds the right click menu to any of your rich edit fields throughout the program.
I'm working in C. Maybe this will be useful for others who use C rather than C++:
HMENU MakeRichEditRightClickMenu(HWND hwndRichEdit) { #define IDC_RIGHT_CLICK_CUT 1 #define IDC_RIGHT_CLICK_COPY 2 #define IDC_RIGHT_CLICK_PASTE 3 HMENU hRichEditRightClickMenu=NULL; CHARRANGE charr; char highlight=0; if(!hwndRichEdit) return NULL; hRichEditRightClickMenu=CreatePopupMenu(); SendMessage(hwndRichEdit,EM_EXGETSEL,0,(LPARAM)&charr); highlight=charr.cpMax>charr.cpMin?1:0; AppendMenu(hRichEditRightClickMenu,MF_STRING,IDC_RIGHT_CLICK_CUT,"Cut"); AppendMenu(hRichEditRightClickMenu,MF_STRING,IDC_RIGHT_CLICK_COPY,"Copy"); AppendMenu(hRichEditRightClickMenu,MF_STRING,IDC_RIGHT_CLICK_PASTE,"Paste"); if(!highlight) { EnableMenuItem(hRichEditRightClickMenu,IDC_RIGHT_CLICK_CUT,MF_GRAYED); EnableMenuItem(hRichEditRightClickMenu,IDC_RIGHT_CLICK_COPY,MF_GRAYED); } if(!IsClipboardFormatAvailable(CF_TEXT)) EnableMenuItem(hRichEditRightClickMenu,IDC_RIGHT_CLICK_PASTE,MF_GRAYED); return hRichEditRightClickMenu; } while(GetMessage(&msg,NULL,0,0)) { if(msg.message==WM_RBUTTONUP) if(msg.hwnd==GetFocus()) { char szClass[64]; GetClassName(msg.hwnd, szClass, sizeof(szClass)); if(strcmpi(szClass,"RichEdit20A")==0) { POINT point; HMENU hMenu=MakeRichEditRightClickMenu(msg.hwnd); floating_popup_init=1; GetCursorPos(&point); if(hMenu) { HWND hwndRichEdit=msg.hwnd; WORD idc=TrackPopupMenu (hMenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_LEFTALIGN|TPM_RETURNCMD , point.x, point.y, 0, hwndMain, NULL ); switch(idc) { case IDC_RIGHT_CLICK_CUT: SendMessage(hwndRichEdit,WM_CUT,0,0); break; case IDC_RIGHT_CLICK_COPY: SendMessage(hwndRichEdit,WM_COPY,0,0); break; case IDC_RIGHT_CLICK_PASTE: SendMessage(hwndRichEdit,WM_PASTE,0,0); break; } DestroyMenu(hMenu); } } } if(TranslateAccelerator(hwndMain,hAccel,&msg)) continue; if(IsDialogMessage(hDlgHelp,&msg)) continue; TranslateMessage(&msg); DispatchMessage(&msg); }
For those using C++ then the new addition is (at least, in the C version o fthe code):
char szClass[64]; GetClassName(msg.hwnd, szClass, sizeof(szClass)); if(strcmpi(szClass,"RichEdit20A")==0)
I expcet it's easy to modify that for C++///
Thanks again for a great article
Robert
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
 |
Does anyone know how to call CreateWindow to make a component that behaves like "CRichEditCtrl" without mfc in win32?
|
| Sign In·View Thread·PermaLink | 1.50/5 (2 votes) |
|
|
|
 |
|
 |
CRect rect(0,0,100,100); CRichEditCtrl richedit; CWnd * pParent = parent window of richedit control CWnd* pWnd = &richedit; pWnd->Create(RICHEDIT_CLASS, NULL, ES_MULTILINE | ES_WANTRETURN, rect, pParent, ID_RICHEDIT);
|
| Sign In·View Thread·PermaLink | 1.00/5 (2 votes) |
|
|
|
 |
|
 |
I found a problem with your code for the TrackPopupMenu(....)
You used TPM_NONOTIFY and TPM_RETURNCMD, that would only work if they chose at that very instant [quite impossible in my mind]. Those 2 commands are pretty much only good for TrackPopupMenuEx which waits until it returns.
Oh, and a quick way to make the menu work : Name your menu commands ID_EDIT_CUT ... and such, the drop-down list works. And subclass CRichEditCommands , this is using WTL, so I'm not quite sure if MFC has it. Those commands there and handlers make it quite easy because you wont have to catch the WM_COMMAND messages as long as you chain the messages [not sure if thats MFC'able .. havent worked much with MFC at all.. but its theory-ish stuff ya could get]
Hope my reply has been useful, because this article was very useful also.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
 |
Thank you for the tips. It is very helpful.
I would suggest people to use the OnNotify method rather than the PreTranslateMessage.
I wrapped an CRichEditCtrl within an ActiveX control and I used the PreTranslateMessage to deal with RBUTTONUP message at first. In some rare combination of mouse clicks(especially double click of RButton), the PreTranslateMessage may not receive the mouse event anymore until the window is repainted. It confused me and I used the Spy++ to track down the message going thru the olecontrol and it did receive the RBUTTONUP message but not going thru PreTranslateMessage . After I changed it to OnNotify, it worked everytime.
|
| Sign In·View Thread·PermaLink | 4.00/5 (1 vote) |
|
|
|
 |
|
 |
Sublcassing the WndProc and intercepting the message WM_CONTEXTMENU does the trick too. The message WM_CONTEXTMENU is automatically sent when the user release the right-click and/or press the context-menu key (VK_APP).
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
#define NA 0 void AppendMenuCommand( INOUT HMENU hMenuResult, UINT idMenuItem, LPCTSTR pszMenuItemText, UINT idMenuInsertAfter = 0) { ASSERT(hMenuResult != NULL); MENUITEMINFO mii; ZeroMemory(OUT &mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID | MIIM_TYPE; mii.wID = idMenuItem; mii.fType = MFT_STRING; mii.dwTypeData = (TCHAR *)pszMenuItemText; BOOL fSuccess = InsertMenuItem(hMenuResult, idMenuInsertAfter, FALSE, IN &mii); ASSERT(fSuccess); } #define EnableMenuCommand(hMenu, idMenuItem, fEnable) ::EnableMenuItem(hMenu, idMenuItem, (fEnable) ? MF_ENABLED : MF_GRAYED)
int RichEdit_DisplayContextMenu(HWND hwndRichEdit) { ASSERT(IsWindow(hwndRichEdit)); HMENU haMenu = ::CreatePopupMenu(); AppendMenuCommand(INOUT haMenu, ID_EDIT_UNDO, "Undo"); AppendMenuCommand(INOUT haMenu, ID_EDIT_REDO, "Redo"); AppendMenuCommand(INOUT haMenu, ID_EDIT_CUT, "Cut"); AppendMenuCommand(INOUT haMenu, ID_EDIT_COPY, "Copy"); AppendMenuCommand(INOUT haMenu, ID_EDIT_PASTE, "Paste");
EnableMenuCommand(haMenu, ID_EDIT_UNDO, SendMessage(hwndRichEdit, EM_CANUNDO, NA, NA)); EnableMenuCommand(haMenu, ID_EDIT_REDO, SendMessage(hwndRichEdit, EM_CANREDO, NA, NA));
DWORD dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE); DWORD ichSelectionStart; DWORD ichSelectionEnd; SendMessage(hwndRichEdit, EM_GETSEL, OUT (WPARAM)&ichSelectionStart, OUT (LPARAM)&ichSelectionEnd); int cchSelection = ichSelectionEnd - ichSelectionStart; EnableMenuCommand(haMenu, ID_EDIT_CUT, cchSelection && (dwStyle & ES_READONLY) == 0); EnableMenuCommand(haMenu, ID_EDIT_COPY, cchSelection);
#define _CF_ALL 0 EnableMenuCommand(haMenu, ID_EDIT_PASTE, SendMessage(hwndRichEdit, EM_CANPASTE, _CF_ALL, NA));
POINT pt; ::GetCursorPos(OUT &pt);
int nCmdId = ::TrackPopupMenu( haMenu, TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, g_hwndMain, NULL);
::DestroyMenu(haMenu); return nCmdId; }
void RichEdit_ExecuteMenuCommand(HWND hwndRichEdit, int nCmdId) { ASSERT(IsWindow(hwndRichEdit)); UINT uMessage = 0; switch (nCmdId) { #ifdef DEBUG default: ASSERT(FALSE && "Unknown command for rich-edit control"); case 0: return; #endif case ID_EDIT_UNDO: uMessage = EM_UNDO; break; case ID_EDIT_REDO: uMessage = EM_REDO; break; case ID_EDIT_CUT: uMessage = WM_CUT; break; case ID_EDIT_COPY: uMessage = WM_COPY; break; case ID_EDIT_PASTE: uMessage = WM_PASTE; break; case ID_EDIT_SELECT_ALL: ::SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); break; case ID_EDIT_CLEAR_ALL: ::SendMessage(hwndRichEdit, WM_SETTEXT, NA, NULL); break; } if (uMessage != 0) ::SendMessage(hwndRichEdit, uMessage, NA, NA); }
void RichEdit_DisplayAndExecuteContextMenuCommand(HWND hwndRichEdit) { RichEdit_ExecuteMenuCommand(hwndRichEdit, RichEdit_DisplayContextMenu(hwndRichEdit)); }
static WNDPROC g_pfnWndProcRichEditOld;
LRESULT CALLBACK WndProcRichEdit(HWND hwndRichEdit, UINT uMsg, WPARAM wParam, LPARAM lParam) { ASSERT(g_pfnWndProcRichEditOld != NULL); switch (uMsg) { case WM_CONTEXTMENU: RichEdit_DisplayAndExecuteContextMenuCommand(hwndRichEdit); return 0; } return ::CallWindowProc(g_pfnWndProcRichEditOld, hwndRichEdit, uMsg, wParam, lParam); }
void RichEdit_Subclass(HWND hwndRichEdit) { ASSERT(IsWindow(hwndRichEdit)); g_pfnWndProcRichEditOld = (WNDPROC)SetWindowLong(hwndRichEdit, GWL_WNDPROC, (LONG)WndProcRichEdit); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
In MicroSoft Word, there is "Show or hide formatting marks" to show or hide the space and newline(Enter key) by chars 0xB6 and 0xB7. How can I achievie this function inside my CRichEditCtrl? Thanks for help.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
 |
When I Build your exemple of popup menu in mode Release, i can use the popup menu with click right but i select exemple copy and i click richedit and select copy with click right and I have a access violation. What is the probleme ? Can you help me ?
 excuse me for my english
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
By using afx_msg LRESULT OnCopy(WPARAM/*wparam*/,LPARAM/*lparam*/);(also for OnSelctAll..) we can fix .
|
| Sign In·View Thread·PermaLink | 1.00/5 (2 votes) |
|
|
|
 |
|