Can't quit TrackPopupMenu - c++

I'm trying to create a popup menu which appear while typing on an edit control to show suggestion. The problem is I can't continue typing as the popup wouldn't disappear. The only way is to click outside the popup. It's every inconvenient if every character you typed you have to click outside to keep writing.
I've tried SetForeGround before calling TrackPopupMenu but it didn't work.
if (HIWORD(wParam) == EN_CHANGE)
{
HMENU suggest = CreatePopupMenu();
WCHAR text[50];
GetWindowText(tagTxt, text, 50);
if (wcslen(text) > 0)
{
for (int i = 0; i < Tag.size(); i++)
{
if (wcsncmp(Tag[i].tagName, text, wcslen(text)) == 0)
AppendMenu(suggest, MF_STRING, NULL, Tag[i].tagName);
}
POINT curPoint;
GetCursorPos(&curPoint);
SetForegroundWindow(hWnd);
UINT code = TrackPopupMenu(suggest,TPM_RETURNCMD | TPM_NONOTIFY, curPoint.x, curPoint.y, 0, hWnd, NULL);
SendMessage(hWnd, WM_NULL, 0, 0);
}
}

Related

After check GetKeyState() in main message loop, application freezes until the second press of the same button

I need to create stop action event in main message loop.(ListBox after press VK_RIGHT moves down - and I don't want it to happen).
Another problem is application freezes until I press right arrow again.
What is happening, why set focus on VK_RIGHT stops widnow from getting messages?
I have Listbox. I want to press VK_RIGHt to move selection to another control(window - here I want it o be hEdit2(in future it will be probaly another ListBox)) But when I do it, cursor of selection moves down - so I added if(GetKeyState(VK_RIGHT)){} And it works but after changing focus to another control application freezes (UNTIL I PRESS ANOTHER TIME VK_RiGHT). wtf?
LRESULT SelIndex;
int rozmiar;
HWND hTmp;
// main message loop
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
// this always works
if ((HWND)msg.hwnd == hWndExit) { return 0; }
// this never tried I guess everything
if ((HWND)msg.hwnd == hWndList) {
SetForegroundWindow(hWndList); hTmp=SetFocus(hWndList); SendMessage(hWndList, WM_SETFOCUS, (WPARAM)hTmp, 0);
AttachThreadInput(GetCurrentThreadId(), GetWindowThreadProcessId(GetAncestor(hWndList, GA_ROOT), NULL), TRUE);
SendMessage(hWndList, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
}
if (GetKeyState(VK_RIGHT)) {
SelIndex = SendMessage(hWndList, LB_GETCURSEL, 0, 0L);
rozmiar = (int)SendMessage(hWndList, LB_GETCOUNT, 0, 0);
// SendMessage(hEdit2, WM_SETFOCUS, (WPARAM)hWndList, 0);
// hTmp = SetFocus(hEdit2);
SetForegroundWindow(hEdit2);
if(SelIndex<rozmiar-1){ SendMessage(hWndList, LB_SETCURSEL, SelIndex-1, 0L); }
else { SendMessage(hWndList, LB_SETCURSEL, SelIndex, 0L); }
}
DispatchMessage(&msg);
}
}
/////////////////////////////////////////////////////////////////////////////////

Handling the hotkey for menu item

I have created a new menu item "Extra" and add it to the window's main menu.
HMENU menu = GetMenu(hWnd);
HMENU popup = CreatePopupMenu();
AppendMenu(menu, MF_STRING | MF_POPUP, (UINT)popup, L"Extra");
SetMenu(hWnd, popup);
Next I inserted new item to "Extra" item with hotkey:
#define IDM_ITEM1 12310
and:
MENUITEMINFOW mmi;
mmi.cbSize = sizeof(MENUITEMINFOW);
mmi.fMask = MIIM_STRING | MIIM_ID;
mmi.fType = MFT_STRING;
mmi.dwTypeData = L"First item\tCtrl+N";
mmi.wID = IDM_ITEM1;
InsertMenuItemW(popup, 1, true, &mmi);
And handle "First item" click:
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDM_ITEM1:
{
MessageBox(0, L"First", L"", MB_OK);
break;
}
}
break;
}
And now when I click on "First item" a message box is appeared.
But when I press Ctrl+N hotkey - nothing happens. Why? How to fix this (without using accelerator resources)?
without using accelerator resources ?
You need to create an array of ACCEL and then call the CreateAcceleratorTable function:
ACCEL s_accel[2] = {{FCONTROL | FVIRTKEY, TEXT('C'), IDM_COLOR},
{FCONTROL | FVIRTKEY, TEXT('Q'), IDM_QUIT}};
HACCEL h_accel = CreateAcceleratorTable(s_accel, 2);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(hwnd, h_accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DestroyAcceleratorTable(h_accel);

CreateWindowEx posts WM_SIZE?

CreateWindowEx API really posts WM_SIZE message?
When I create a window via CreateWindowEx as full screen mode,
CreateWindowEx posts WM_SIZE but window mode doesn't.
My code sets the window style like this :
if(bFullscr)
{
//When the window is in full screen mode.
nStyle = WS_POPUP;
nExtraStyle = WS_EX_APPWINDOW;
}
else
{
//Otherwise.
nStyle = WS_OVERLAPPEDWINDOW;
nExtraStyle = (WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
}
And changes display settings like this (full screen mode only) :
if(bFullscr)
{
DEVMODE sScrSet;
memset(&sScrSet, 0, sizeof(DEVMODE));
sScrSet.dmSize = sizeof(DEVMODE);
sScrSet.dmPelsWidth = nWidth;
sScrSet.dmPelsHeight = nHeight;
sScrSet.dmBitsPerPel = nColorBit;
sScrSet.dmFields = (DM_BITSPERPEL | DM_PELSHEIGHT | DM_PELSWIDTH);
if(ChangeDisplaySettings(&sScrSet, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
//Error routine.
}
}
I'm really wonder why CreateWindowEx posts WM_SIZE message selectively.
If you simply want to resize the window, somewhere in your code you should have ShowWindow(hWnd, nCmdShow); change it as follows:
ShowWindow(hWnd, SW_SHOWDEFAULT);//show normal
ShowWindow(hWnd, SW_SHOWMAXIMIZED);//show maximized (full screen)
SetWindowPos(hWnd, NULL, 10, 10, 300, 300, SWP_SHOWWINDOW);//show at specific position
Also you could use WS_MAXIMIZE in CreateWindow, but that could complicate things. Window usually has WS_OVERLAPPEDWINDOW or WS_POPUP|WS_CAPTION|WS_SYSMENU. You should pick one and keep it simple.
When Window size changes, it receives WM_SIZE, you can catch that and examine it.

Right Click on ListView WinAPI C++

I have a window with a listview which I would like to right click an entry in the listview and have certain options displayed in a context menu. I cant find any examples for C++. The similar question is here but this is for listbox and i cant make it work in my program
So far I could not find any examples for c++ online. I could make it work for WM_LBUTTONDOWN on my main window but not on listview. Anyway, this is my code which i cant make it work:
case WM_CONTEXTMENU:
if ((HWND)wParam == hWndListView) {
POINT cursor;
GetCursorPos(&cursor);
TrackPopupMenu((HMENU)GetSubMenu(LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT)), 0), TPM_LEFTALIGN | TPM_RIGHTBUTTON, cursor.x, cursor.y, 0, hWnd, NULL);
}
break;
Any advise? Thanks
Answer:
case WM_NOTIFY:
// When right button clicked on mouse
if ((((LPNMHDR)lParam)->hwndFrom) == hWndListView)
{
switch (((LPNMHDR)lParam)->code)
{
case NM_RCLICK:
{
POINT cursor; // Getting the cursor position
GetCursorPos(&cursor);
// Creating the po-up menu list
TrackPopupMenu((HMENU)GetSubMenu(LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT)), 0), TPM_LEFTALIGN | TPM_RIGHTBUTTON, cursor.x, cursor.y, 0, hWnd, NULL);
}
break;
}
break;
}
break;
This works under WndProc

Get text in a listbox control added to a child window

I want to add the text in a listbox control to the child of my main window. The child is essentially an edit control, but is not a dialog. I have tried a few different functions already with no success, I believe my problem is that I need to somehow switch focus from the dialog window to the child window before adding the text. I would prefer not to get an answer with specific code, but if I could be pointed to a helpful function or concept, that would be great!
EDIT: The listbox is part of a larger dialog window that allows the user to enter text and then add it to the list. These functions are working very well. What I'd like to do is get the text that is added to the list moved into the child window when the user clicks a button on the dialog, preferably without the user having to select the items before clicking the button.
There's a lot of code, but I think these pieces are relevant:
Child window:
case WM_CREATE:
{
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
0, 0, 100, 100, w, (HMENU) IDC_EDIT, NULL, NULL);
if (hEdit == NULL){
MessageBox(NULL, "Could not create child window :(", "ERROR", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
}
break;
case WM_SIZE:
{
RECT wSize;
GetClientRect(w, &wSize);
SetWindowPos(hEdit, NULL, 0, 0, wSize.right, wSize.bottom, NULL);
}
Function to add text to child window by clicking a button on the dialog (HWND hEdit, the child window, is defined globally):
case ID_ADDMAIN:
{
HWND hList = GetDlgItem(w, IDC_LIST1);
int count = SendMessage(hList, LB_GETCOUNT, NULL, NULL);
if (count > 0){
DWORD textLength = GetWindowTextLength(hList);
LPSTR alloc;
alloc = (LPSTR) GlobalAlloc(GPTR, textLength + 1);
if(GetWindowText(hList, alloc, textLength + 1)){
SendMessage(hEdit, WM_SETTEXT, NULL, (LPARAM) alloc);
}
GlobalFree(alloc);
}
else{
MessageBox(NULL, "There's nothing to add!", "???", MB_ICONINFORMATION | MB_OK);
}
}
break;
Besides the SendMessage function, I also tried SetWindowText and I tried getting each string in the listbox separately using a for loop rather than GetWindowText. Thank you in advance for your help.
You misunderstand the list box. The LB_GETCOUNT message is used to get the number of items (rows) in the list box. (Not the number of characters.)
GetWindowText is not appropriate for a list box: It gets the titlebar text, but a list box has no titlebar. What you can do with a list box is find out which row is selected (LB_GETCURSEL) and then get the text from that row (LB_GETTEXT).
Here is a list of functions I wrote for my WINAPI class library..
int ListBox::GetItemCount() const
{
return SendMessage(Control::GetHandle(), LB_GETCOUNT, 0, 0);
}
int ListBox::GetSelectedIndex() const
{
return SendMessage(Control::GetHandle(), LB_GETCURSEL, 0, 0);
}
void ListBox::SetSelectedIndex(int Index)
{
SendMessage(Control::GetHandle(), LB_SETCURSEL, Index, 0);
}
void ListBox::AddItem(const tstring &Item, int Index)
{
SendMessage(Control::GetHandle(), Index == 0 ? LB_ADDSTRING : LB_INSERTSTRING, Index, reinterpret_cast<LPARAM>(Item.c_str()));
}
void ListBox::RemoveItem(int Index)
{
SendMessage(Control::GetHandle(), LB_DELETESTRING, Index, 0);
}
void ListBox::Clear()
{
SendMessage(Control::GetHandle(), LB_RESETCONTENT, 0, 0);
}
int ListBox::GetIndexOf(const tstring &Item)
{
return SendMessage(Control::GetHandle(), LB_FINDSTRINGEXACT, -1, reinterpret_cast<LPARAM>(Item.c_str()));
}
int ListBox::GetIndexPartial(const tstring &Item)
{
return SendMessage(Control::GetHandle(), LB_FINDSTRING, -1, reinterpret_cast<LPARAM>(Item.c_str()));
}
void ListBox::SetColumnWidth(int Width)
{
SendMessage(Control::GetHandle(), LB_SETCOLUMNWIDTH, Width, 0);
}
tstring ListBox::GetItem(int Index) const
{
std::vector<char> Buffer(SendMessage(Control::GetHandle(), LB_GETTEXTLEN, Index, 0) + 1);
SendMessage(Control::GetHandle(), LB_GETTEXT, Index, reinterpret_cast<LPARAM>(Buffer.data()));
return tstring(Buffer.begin(), Buffer.end());
}
You'll need:
ListBox::GetItem(ListBox::GetSelectedIndex());
to get the text of the current item in the list box..
For the Edit control, I have:
void TextBox::SetReadOnly(bool ReadOnly)
{
SendMessage(Control::GetHandle(), EM_SETREADONLY, ReadOnly, 0);
}
void TextBox::SetPassword(bool Enabled, char PasswordChar)
{
SendMessage(Control::GetHandle(), EM_SETPASSWORDCHAR, Enabled ? PasswordChar : 0, 0);
}
std::uint32_t TextBox::GetTextLength() const
{
return GetWindowTextLength(Control::GetHandle());
}
void TextBox::ShowScrollBar(bool Show, int wBar)
{
::ShowScrollBar(Control::GetHandle(), wBar, true);
}
void TextBox::AppendText(const tstring &Text) const
{
SendMessage(Control::GetHandle(), EM_SETSEL, -1, -1);
SendMessage(Control::GetHandle(), EM_REPLACESEL, 0, reinterpret_cast<LPARAM>(Text.c_str()));
}
tstring TextBox::GetText() const
{
std::vector<TCHAR> Buffer(GetWindowTextLength(Control::GetHandle()) + 1);
GetWindowText(Control::GetHandle(), Buffer.data(), Buffer.size());
return tstring(Buffer.begin(), Buffer.end());
}
void TextBox::SetText(const tstring &Text)
{
this->Title = Text;
SetWindowText(Handle, Text.c_str());
}
You can get the text from the edit box easily with these.. I hope these aid you greatly..