Handling arrow key in cedit control of mfc - mfc

I'm making matlab-like command window using cedit control of mfc.
For example, after I input several commands, I want to display old command using an arrow key (specifically the up key).
I succeeded in displaying old commands, but failed to locate the cursor on the end of this command.
The reason seems that the arrow key was input one more time after I locate the cursor in the end of this command.
Here is detailed situation.
First I input command 'play'
and then Play!.. message pops up.
and in the next command prompt I hit '↑' key
and I succeded the old command 'play' streamed automatically,
However, my cursor go up to upper line.
# play
Play!.. | (← cursor located here..)
# play| (← I want to locate the cursor here, after hitting '↑' key)
This is my code:
class CEditCommand::CEdit
{
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
}
BOOL CEditCommand::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_UP)
{
int nS = 0; nE =0;
GetSel(nS, nE);
int nLineIndex = LineIndex();
CString str = m_CommandHistory[m_nCommandIndex];
m_nCommandIndex--;
SetSel(nLineIndex, nE);
ReplaceSel(str);
SetSel(0, -1);
SetSel(-1, -1);
}
}
}
I don't know why '↑' key would be pushed again after executing PreTranslateMessage.
Does anyone have an idea about this?

Your edit control will still get the up-arrow message, so you will need to return TRUE in CEditCommand::PreTranslateMessage() for both WM_KEYDOWN and WM_KEYUP when pMsg->wParam == VK_UP

Related

Ignoring the status of Shift key when detecting shortcuts via TranslateAccelerator

I'm stuck: How do I prevent the virtual Shift key from being considered during accelerator translation? For instance, if my accelerator table contains a single entry of Ctrl+A (to select everything in currently focused window, say), then I would want the corresponding command be detected when Ctrl+A is pressed as well as if Ctrl+Shift+A is pressed (but contrary, I still want the Alt key to play its role, so Ctrl+Shift+A shound't translate to my command).
This post didn't help. I think I'm calling the TranslateAccelerator function while I'm still in the message loop (see code below and correct me if I'm wrong).
I tried to outsmart Windows by getting the current keyboard status when I see a WM_KEYDOWN message, manually change the virtual Shift key status to "not pressed," do the accelerator translation, and revert my manual modifications. In code:
BOOL CHexaEditor::PreTranslateMessage(PMSG pMsg){
// pre-processing the Message
if (::GetFocus()==m_hWnd){ // I'm not sure if this is mandatory in MFC
if (pMsg->message==WM_KEYDOWN){
BYTE keyboardState[256];
::GetKeyboardState(keyboardState);
const BYTE shiftState0=keyboardState[VK_SHIFT];
keyboardState[VK_SHIFT]=0; // 0 = "not pressed"
const BOOL result=::TranslateAccelerator(m_hWnd,hDefaultAccelerators,pMsg);
keyboardState[VK_SHIFT]=shiftState0;
::SetKeyboardState(keyboardState);
return result;
}else
return ::TranslateAccelerator(m_hWnd,hDefaultAccelerators,pMsg);
}else
return FALSE;
}
No success so far, any help highly appreciated. Thanks :-)
So the correct answer is (thanks to ybungalobill above):
BOOL CHexaEditor::PreTranslateMessage(PMSG pMsg){
// pre-processing the Message
if (::GetFocus()==m_hWnd){
if (pMsg->message==WM_KEYDOWN){
BYTE keyboardState[256];
::GetKeyboardState(keyboardState);
const BYTE shiftState0=keyboardState[VK_SHIFT];
keyboardState[VK_SHIFT]=0;
/* --> */ ::SetKeyboardState(keyboardState); // this line was missing
const BOOL result=::TranslateAccelerator(m_hWnd,hDefaultAccelerators,pMsg);
keyboardState[VK_SHIFT]=shiftState0;
::SetKeyboardState(keyboardState);
return result;
}else
return ::TranslateAccelerator(m_hWnd,hDefaultAccelerators,pMsg);
}else
return FALSE;
}

Is there an event that fires from a C++ program when a control is about to lose focus?

I am trying to fix a validation bug in a MFC CEdit control. Currently, validation is performed in an OnChange event handler. But this does not work because it validates data before the user is finished entering it.
So, instead, I am trying to validate inside an OnKillFocus event handler. If validation fails, then I use GotoDlgCtrl() to return focus to the edit box that contained the invalid data. And when I call GotoDlgCtrl(), the kill focus event fires again, and I'm in an infinite loop.
So, I'd like to handle an event that fires just before the control loses focus, so that if I determine that the data is invalid, I can stop focus from leaving and instead get the user to enter correct data.
I know I've seen a Validating event someplace, but that was probably in the .Net world. But it offers the functionality I'm looking for.
Right-click the dialog resource and invoke Class Wizard:
Next, go to the Virtual Functions tab, locate PreTranslateMessage and add it:
Then, you can do something like this:
BOOL CTestDlgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_CHAR)
{
CWnd *pControl = GetDlgItem(IDC_EDIT1);
if (pControl->GetSafeHwnd() == pMsg->hwnd)
{
if (pMsg->wParam == _TINT('!'))
{
AfxMessageBox(_T("Not allowed ! character"));
return TRUE;
}
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
Normally the control is a member variable of type CEdit so you could compare against m_edit.GetSafeHwnd() instead.
Results:
Update
I realise you stated:
But this does not work because it validates data before the user is finished entering it.
You could use WM_KEYUP instead:
BOOL CTestDlgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYUP)
{
CWnd *pControl = GetDlgItem(IDC_EDIT1);
if (pControl->GetSafeHwnd() == pMsg->hwnd)
{
CString str;
GetDlgItemText(IDC_EDIT1, str);
if (str.Find(_T("!")) >= 0)
{
AfxMessageBox(_T("Not allowed ! character"));
return TRUE;
}
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
That is give you a chance to validate after the display has been updated.
An alternative it to customize your DoDataExchange handler. In there you can validate as required. Then in your code you simple test the return value of UpdataData(TRUE) for FALSE.

CMFCToolBarComboBoxEdit handle delete button

CMFCToolBarComboBoxEdit handles the BackSpace button but it doesn't handle the delete button.
Is there any way to handle the delete button except PreTranslateMessage?
if yes, what is this way?
if no, then how can I get the current cursor position in the control and how to remove specific char using its index so I can remove the char which on the right of the cursor if nothing is selected?
Thanks in advance.
Yes use, PreTranslateMessage. If you detected the sequence that should be handled, call:
if (..) // Check if you have a message that should
// be passed to the window directly
{
TranslateMessage(pMsg);
DispatchMessage(pMsg);
return TRUE;
}
You can do this always in PreTranslateMessage, when you detect that the message should be handled by the default control, and should not be handled by any other control in the chain of windows that execute PreTranslateMessage. This is also helpful if you have a combo box open and want the Page Down/Up handled internally and not by the view or any accelerator.
I've handled the delete key in the PreTranslateMessage as follows:
BOOL PreTranslateMessage(MSG* pMsg)
{
if(WM_KEYDOWN == pMsg->message && VK_DELETE == pMsg->wParam)
{
int iStartChar = -1, iEndChar = -1;
GetSel(iStartChar, iEndChar);
if(iStartChar != iEndChar)
Clear(); //clear the selected text
else
{
SetSel(iStartChar, iStartChar + 1);
Clear();
}
}
return CMFCToolBarComboBoxEdit::PreTranslateMessage(pMsg);
}

Text Selection with CRichEditCtrl in MFC

I have CRichEditCtrl object which is read only(Text is for read and not allowed to modified). I want to provide functionality of Text Selection with Mouse for text displayed with CRichEditCtrl object.
Following Code is working to capture event for Left Mouse Button - DOWN & UP
BOOL CReportFormView::PreTranslateMessage(MSG* pMsg)
{
if (m_EditNs->GetFocus()!=NULL)
{
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
return TRUE;
case WM_LBUTTONUP:
return TRUE;
}
}
}
Now looking for some code to write in case block which will highlight selected text. I want to know if there is any API available for CRichEditCtrl object which help to track at which location user pressed Left Mouse Button and released
You could use member function CString strText = m_myRichEditCtrl.GetSelText(); or some other member function. Like GetSel() just a suggestion.
I think you will need to use the EM_CHARFROMPOS message. ex. some form of this:
POINTL pt { x,y }; // ... screen coordinates to test relative to edit
DWORD info = m_EditNS->SendMessage(EM_CHARFROMPOS, 0, pt);
int charIndex = LOWORD(info);
int line = HIWORD(info);
After that, set the selection with normal selection methods.
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761566(v=vs.85).aspx

Delete Key is not triggering KeyUp & KeyDown Event

I am currently dealing with a multi-form application and am having issue registering a del key press, the application that requires the del key is a form with a frame on it with objects painted on it that can be selected, upon pressing the del key the selected objects are to be deleted via a deleteObjects method. The code I am currently using is as follows
void __fastcall TF_Image::KeyUpKbd( WORD &Key )
{
if(Key == VK_DELETE || Key == VK_DKEY)
{
deleteSelectedObjects();
}
}
(Note: There are other paramenters in the function call but they aren't used)
TF_Image inherits from TFrame
I have tried mapping other keys other than the del key ie the D key and have found that the method is called with no problem. I have discovered that when pressing (physically) the del key the methods associated with KeyUp & KeyDown are not called.
Edit: So i've attempted to add the DeleteSelectedOb() method to my WndProc method without much luck either.
void __fastcall TF_ImgBrowserOA::WndProc(TMessage &Message)
{
if (Message.Msg == WM_KEYDOWN)
{
if (Message.WParam == VK_DELETE)
{
F_Image->DeleteSelectedOb();
}
}
//code that manages window resize
TForm::WndProc(Message);
}
The WndProc method doent appear to respond to keystrokes
So after cleaning up some code in some other modules and removing unneccessary menu's I decided to go back and look at this section again after I found a similar piece of code implementing a similar function, I couldn't see much difference between them and so I recompiled and attempted to run my Delete function from the KeyDown event and for some reason it just worked, I suspect it came down to an issue of another element holding focus in the Application. As a precaution I also called a SetFocus() to the frame in which I required this code to operate in. Its still a mystery to me why this didn't work intially though.
Here is a snippet for my TRichEdit control (Script_Edit).
TWndMethod *PrevWndProc = Script_Edit->WindowProc;
Script_Edit->WindowProc = MyWndProc;
void __fastcall My_Form::MyWndProc(TMessage &Message) {
switch (Message.Msg) {
case WM_KEYDOWN: {
// Check for DELETE and BACKSPACE keys
if( Message.WParam == VK_BACK ||
Message.WParam == VK_DELETE
) {
// Do whatever you need
}
break;
default:
// call default handler if not processed
PrevWndProc(Message);
}
}
You can't get much closer to the message core than this with VCL...