|
|
gee thanks for that
to quote the second last line
"Yea - though there are probably other ways of doing this these..."
Bryce
|
|
|
|
|
Struggled for 3 days and your article is the only one showing MSGFILTER. You save my day!!! Thanks a million.
|
|
|
|
|
just wanted to say thanks. hope you'll keep writing.
|
|
|
|
|
well when i get time and have something of interest to write about
I'm pleased you liked it
Bryce
|
|
|
|
|
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;
}
|
|
|
|
|
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.
|
|
|
|
|
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
|
|
|
|
|
check out to what these macros are expanded. You got to understand it carefully.
Greetings from Germany
|
|
|
|
|
|
I came up with the following solution. Override the virtual function GetContextMenu() within your CRichEditView derived class and add the following code.
<br />
CRichEditCtrl& Edit = GetRichEditCtrl();<br />
<br />
CString strMenu[] =<br />
{<br />
"&Undo", "Cu&t", "&Copy", "&Paste", "&Delete", "Select &All"<br />
};<br />
int nCmd[] = {WM_UNDO, WM_CUT, WM_COPY, WM_PASTE, WM_CLEAR, EM_SETSEL};<br />
CMenu menu;<br />
HMODULE hUser32 = LoadLibrary("USER32");<br />
if (hUser32)<br />
{<br />
if (menu.Attach(LoadMenu(hUser32, MAKEINTRESOURCE(1))))<br />
{<br />
for (int i = _countof(nCmd); --i >= 0;)<br />
VERIFY(menu.GetMenuString(nCmd[i], strMenu[i], MF_BYCOMMAND) != 0);<br />
}<br />
menu.Detach();<br />
FreeLibrary(hUser32);<br />
}<br />
<br />
menu.CreatePopupMenu();<br />
menu.AppendMenu(MF_STRING, ID_EDIT_UNDO, strMenu[0]);<br />
menu.AppendMenu(MF_SEPARATOR);<br />
menu.AppendMenu(MF_STRING, ID_EDIT_CUT, strMenu[1]);<br />
menu.AppendMenu(MF_STRING, ID_EDIT_COPY, strMenu[2]);<br />
menu.AppendMenu(MF_STRING, ID_EDIT_PASTE, strMenu[3]);<br />
menu.AppendMenu(MF_STRING, ID_EDIT_CLEAR, strMenu[4]);<br />
menu.AppendMenu(MF_SEPARATOR);<br />
menu.AppendMenu(MF_STRING, ID_EDIT_SELECT_ALL, strMenu[5]);<br />
<br />
CallPopupMenuUpdateHandlers(&menu, this);<br />
<br />
HMENU hMenu = menu.Detach();<br />
return hMenu;<br />
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:
<br />
void CallPopupMenuUpdateHandlers(CMenu* pMenu, CCmdTarget* pTarget)<br />
{<br />
<br />
CCmdUI state;<br />
state.m_pMenu = pMenu;<br />
ASSERT(state.m_pOther == NULL);<br />
ASSERT(state.m_pParentMenu == NULL);<br />
<br />
state.m_nIndexMax = pMenu->GetMenuItemCount();<br />
for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;<br />
state.m_nIndex++)<br />
{<br />
state.m_nID = pMenu->GetMenuItemID(state.m_nIndex);<br />
if (state.m_nID == 0)<br />
continue;
<br />
ASSERT(state.m_pOther == NULL);<br />
ASSERT(state.m_pMenu != NULL);<br />
if (state.m_nID == (UINT)-1)<br />
{<br />
state.m_pSubMenu = pMenu->GetSubMenu(state.m_nIndex);<br />
if (state.m_pSubMenu == NULL ||<br />
(state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||<br />
state.m_nID == (UINT)-1)<br />
{<br />
continue;
}<br />
state.DoUpdate(pTarget, FALSE);
}<br />
else<br />
{<br />
state.m_pSubMenu = NULL;<br />
state.DoUpdate(pTarget, true);<br />
}<br />
<br />
UINT nCount = pMenu->GetMenuItemCount();<br />
if (nCount < state.m_nIndexMax)<br />
{<br />
state.m_nIndex -= (state.m_nIndexMax - nCount);<br />
while (state.m_nIndex < nCount &&<br />
pMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)<br />
{<br />
state.m_nIndex++;<br />
}<br />
}<br />
state.m_nIndexMax = nCount;<br />
}<br />
}<br />
Hope this code is helpfull to some of you!
Marc
|
|
|
|
|
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
|
|
|
|
|
Is it possible to insert a control, like an edit box, in a popup menu item?
|
|
|
|
|
Thanks again
|
|
|
|
|
You can use User32.dll for have an international Context Menu
<br />
void RichEdit_ContextMenu(HWND hwndRichEdit){<br />
<br />
BOOL bCandUndo=SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);<br />
BOOL bCandRedo=SendMessage(hwndRichEdit, EM_CANREDO, 0, 0);<br />
<br />
DWORD dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);<br />
DWORD ichSelectionStart;<br />
DWORD ichSelectionEnd;<br />
SendMessage(hwndRichEdit, EM_GETSEL, OUT (WPARAM)&&RichSelectionStart, OUT (LPARAM)&&RichSelectionEnd);<br />
int cchSelection = ichSelectionEnd - ichSelectionStart;<br />
<br />
BOOL bCanCut=(cchSelection && !(dwStyle&ES_READONLY));<br />
BOOL bCanCopy=(cchSelection);<br />
BOOL bCanPaste=SendMessage(hwndRichEdit, EM_CANPASTE, 0, 0);<br />
<br />
HMODULE hUser32=LoadLibrary("USER32");<br />
<br />
char Strings[6][100]={"&Undo","Cu&t","&Copy","&Paste","&Delete","Select &All"};<br />
int Ids[6]={WM_UNDO,WM_CUT,WM_COPY,WM_PASTE,WM_CLEAR,EM_SETSEL};<br />
<br />
if (hUser32){<br />
HMENU hMenu=LoadMenu(hUser32,(LPCSTR)1);<br />
for (int i=5;i>=0;--i)<br />
GetMenuItemString(hMenu,Ids[i],false,Strings[i],100);<br />
FreeLibrary(hUser32);<br />
}<br />
<br />
HMENU hMenu = CreatePopupMenu();<br />
AppendMenuEx(hMenu,MF_STRING|(bCandUndo?0:MF_DISABLED),Ids[0],Strings[0]);<br />
AppendMenu(hMenu,MF_SEPARATOR,0,0);<br />
AppendMenuEx(hMenu,MF_STRING|(bCanCut?0:MF_DISABLED),Ids[1],Strings[1]);<br />
AppendMenuEx(hMenu,MF_STRING|(bCanCopy?0:MF_DISABLED),Ids[2],Strings[2]);<br />
AppendMenuEx(hMenu,MF_STRING|(bCanPaste?0:MF_DISABLED),Ids[3],Strings[3]);<br />
AppendMenu(hMenu,MF_STRING,Ids[4],Strings[4]);<br />
AppendMenu(hMenu,MF_SEPARATOR,0,0);<br />
AppendMenu(hMenu,MF_STRING,Ids[5],Strings[5]);<br />
<br />
POINT pt;<br />
GetCursorPos(OUT &pt);<br />
<br />
int nCmdId = TrackPopupMenu(hMenu,TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,pt.x, pt.y,0, hwndRichEdit, NULL);<br />
<br />
DestroyMenu(hMenu);<br />
<br />
switch (nCmdId) {<br />
case EM_UNDO:<br />
case WM_CUT:<br />
case WM_COPY:<br />
case WM_PASTE:<br />
case WM_CLEAR:<br />
case EM_SETSEL:<br />
SendMessage(hwndRichEdit, nCmdId, 0, -1);<br />
break;<br />
}<br />
}<br />
|
|
|
|
|
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.
|
|
|
|
|
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
|
|
|
|
|
Does anyone know how to call CreateWindow to make a component that behaves like "CRichEditCtrl" without mfc in win32?
|
|
|
|
|
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);
|
|
|
|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
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).
|
|
|
|
|
Can you provide an example of how to do this, please?
Thanks in advance.
|
|
|
|
|
#define NA 0 // Not Applicable. The parameter has a value of zero because it does not apply to the context of the other parameters.
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 // All the clipboard formats accepted by the rich-edit
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);
}
|
|
|
|
|
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.
|
|
|
|
|