How can I make a CMenu TrackPopupMenu SubMenu menu-item clickable? - windows

Normally when you hover over a sub-menu (with the little arrow) on a CMenu menu item it delays briefly then shows the sub-menu items. Also, if you click the item before the delay timeout, it shows the sub menu items. I want the delay behavior, but I want a different behavior for the click. That is, I want the sub-menu itself (the one with the arrow) to be a clickable entity too, i.e. it has an ID and results in a WM_COMMAND and menu dismissal.
The idea is, the main sub-menu menu item is a "default", and the sub-menu items are modified versions, e.g. "print->" (defaulting to default printer), and sub-menu items like "print preview" "print to file" etc. Thanks for thoughts/suggestions.
edit:
IDR_MY_MENU MENUEX
BEGIN
POPUP "menu"
BEGIN
MENUITEM "&Something Else", ID_MENU_SOMETHING_ELSE
POPUP "&Print", ID_MENU_PRINT
BEGIN
MENUITEM "Print Pre&view", ID_MENU_PRINT_PREVIEW
MENUITEM "Print to &File", ID_MENU_PRINT_TO_FILE
END
MENUITEM "", -1, MFT_SEPARATOR
MENUITEM "&Bottom", ID_MENU_BOTTOM
MENUITEM "&Done", ID_MENU_DONE
END
END

I dont know if theres a better way as I last did this 2 years ago, but the way I solved the problem had one constraint: that you own the whole menu. If you do own the whole menu what you can do is create two columns (two columns in the menu/submenu that is, not a new submenu) and use the right column as a submenu and the left column as the default.

For future StackOverflowers, here's what I did...
Added a handler for OnInitMenuPopup and corresponding
ON_WM_INITMENUPOPUP(), although doing this messed up my UPDATE_COMMAND_UI handler but I was able to resolve that by moving that code into the OnInitMenuPopup handler.
In the OnInitMenuPopup handler, set a hook for the mouse SetWindowsHookEx(WH_MOUSE,...) (checking if one isn't already set because sub-menus can cause multiple calls)... and for each menu that initializes during the hook, push the HMENU onto a linked list.
In the mouse hook proc, checking the end of the list and working toward the front, verify each HMENU is still and active menu via IsMenu and remove it if it isn't. If there are no menus left, UnhookWindowsHook.
If there are menus left in the hook proc, check for WM_LBUTTONUP or WM_RBUTTONUP and see if happened over one of your menu items (because of coordinate mapping of screen versus sub-menu that I couldn't figure out I ended up simply cycling through the menu items via GetMenuItemRect and checking PtInRect to determine this).
If there is a click hit, do GetMenuItemInfo on the item, and if there's a hSubMenu for that item and it has an wID (and the wID doesn't match the sub-menu handle), simply post the id as a WM_COMMAND, post a WM_CANCELMODE to dismiss the menu, and disable the hook... Bingo!
So, seems to work fine and everything else functions like normal. The only issue at this point is a keyboard handler for selecting the item instead opening the sub-menu but I suspect the same idea works there as well.
Also, I added the main sub-menu text as the first item in the sub-menu list with a separator which added some clarity to what the menu was doing.

Related

MFC: CListView: Why SetItemState(-1, 0, LVIS_SELECTED) doesn't work if multiple items were selected with SHIFT + arrow key?

Easy to reproduce problem using default MFC dialog application:
Add listview control and link it to CListCtrl class - m_listCtrl.
Enable "owner data" or LVS_OWNERDATA flag to enable virtual list view mode. Enable multiple selection and "Always show selection".
In OnInitDialog() add at least one column and call m_listCtrl.SetItemCount() to set number of items in the list to more than one.
Link button click with a method that calls m_listCtrl.SetItemState(-1, 0, LVIS_SELECTED); That call should deselect all entries in the list.
Click on item in the list and use SHIFT + arrow key to extend selection to at least two entries. If selection is extended with mouse or CTRL + click or CTRL + SHIFT, then the issue doesn't appear.
Click on a button that triggers SetItemState() call in step (4) and notice that items don't get deselected the first time. They only get deselected the second time.
Is that a bug in listview control or am I doing something wrong?
Calling m_listCtrl.SetItemState(-1, 0, LVIS_SELECTED); should deselect all entries with the first call, but it does so only when called twice.

Is it possible to see where control focus is being lost to?

I have a ClistCtrl (wraps Windows list view for non-MFC users) on a dialog. I set functionality that when the list loses focus, any selected items are unselected.
I also have a "remove items" button, whose on-click handler will delete any selected items in the list.
The idea is you select items in the list, and then either click the button to remove these items, or click somewhere else and the selection is cancelled.
But, when you click the delete button, the list loses focus first and therefore nothing happens! Is there a way around this?
You can receive message about lost focus with WM_KILLFOCUS, its wParam will give you a handle to window which got focus:
wParam
A handle to the window that receives the keyboard focus. This
parameter can be NULL.
You should be able to use Spy++ to see WM_KILLFOCUS on you list window, and read its wParam - and later find also with spy++ which window is it.
As said in other posts, you can use WM_KILLFOCUS for that.
But I think it's a very bad idea to clear the selection on losing focus.
Just imagine: the user selects a whole bunch of items using multiselect (using shift, ctrl, scrollbar..), and then, one of the following happens:
The phone rings, urgent call - the user needs to check a mail: selection: gone!
An annoying message box pops up taking focus (yes, it does happen): selection -> gone.
Your users might hate you for this, so don't do it :) (not even if there are only 3 items in the listcontrol).
The usual way is to gray the selection on losing focus. You could add a 'clear selection' button, but even that isn't needed. Just clicking on one item will clear the selection (except for that one item of course).
Bottom line: don't clear the selection on losing focus, ever.
Update:
If the selection is not visible on losing focus, the LVS_SHOWSELALWAYS flag is what you need:
LVS_SHOWSELALWAYS
The selection, if any, is always shown, even if the control does not
have the focus.

Delphi FMX TMainMenu first menu item doesn't show in OSX, OK in WIndows

I created a Multi-Device Application in Delphi Seattle and added a TMainMenu with MenuItem1 and MenuItem2 (nothing else). Building and running for OSX, only MenuItem2 shows. Building and running for Windows, both menu items show. I didn't treat the two menu items any differently. Both were just added and not customized in any way. Anyone else experience this?
The top level items in a Mac menu bar typically look like this:
Apple/system menu ('About this Mac', 'System Preferences...', etc.)
Application menu ('About XXX', 'Services', 'Hide XXX', 'Hide Others',
'Show All', 'Quit XXX')
File menu
Edit Menu
...
In FMX (excepting the very first version), the first top level item in a TMainMenu represents the application menu. As such, you need to add an item before File (or whatever is your current first item) that is only show on OS X; its sub-items should then include the usual items of a Mac application menu. To implement those items' behaviour, you can utilise standard actions (TFileHideApp, TFileHideAppOthers, TFileExit). Or, in more detail:
If there isn't one already, add a TActionList component to the form
Double click the action list to bring up the action list editor
Click on the add button's dropdown arrow, and select New Standard Action...
Select all of TFileHideApp, TFileHideAppOthers and TFileExit, and click OK
Back on the form, open up the menu editor by double clicking on the TMainMenu component
Add an item immediately before the first one that currently exists; in the Object Inspector, call it (say) mnuApp
Add at least four child items to mnuApp; using the Object Inspector, assign the Action property of the first to FileHideApp1, the second to FileHideAppOthers, and the fourth to FileExit1; set the Text of the third to a hyphen (-) to make it a separator
Close the menu editor
If one doesn't already exist, create a OnCreate handler for the form by double clicking its entry in the Object Inspector
Add to FormCreate a line to hide mnuApp if not running on OS X. Conversely, if you already have a menu item for File|Exit, this should be hidden if running on OS X since the functionality is now Quit under the application menu:
...
procedure TForm1.FormCreate(Sender: TObject);
begin
mnuApp.Visible := (TOSVersion.Platform = pfMacOS);
itmExit.Visible := (TOSVersion.Platform <> pfMacOS);
end;

RadTreeView NodeEditing change behaviour

By default RadTreeNode becomes editable after (http://www.telerik.com/help/aspnet-ajax/p_telerik_web_ui_radtreeview_allownodeediting.html)
"End-users can edit the text of tree-nodes by pressing F2 when the node is selected or by clicking on a node that is already selected (slow double click)."
This is unconvenient, that whatever was clicked twice start being edited, even if clicks are spearated by far.
How I can change its behaviour so edit happens on normal, fast double-click?
Turns out, you just have to rely on context menu approach, and start editing item from context menu handler. AllowNodeEditing="False" can be set, yet OnNodeEdit="HandleNodeEdit" and the like will still work, when editing is triggered manually using appropritate client API function from context menu handler function.

Windows Menu: MF_HILITE flag does not cleared

I have CMenu instance on which I add multiple items. For one of the items I added in it, I set the MF_HILITE flag.
When I show the menu, the appropriate item get hi-lighted correctly, as requested. The problem is that it stays hi-lighted until I move the mouse over it and leave. I only want one item to be hi-lighted at the time. It seems that Windows does not un-light it when another item is hi-lighed.
How could I force it to be un-lighted as soon as another item get the hi-light? I could not find any mouse-over callback or message for the menu, and I could not find a invalidate either.
You're using MF_HILITE in a weird way. The item isn't actually highlighted, it's just drawn like it is. If the user presss enter, the "highlighted" item won't be selected.
You're probably looking for MF_DEFAULT.
It does not appear to be possible.
The internal state for the currently selected item in the menu can't be set. Using the MF_HILITE or HiliteMenuItem does not set the currently selected item, it only sets the visual style.
As a work-around, I have used a popup ListBox instead, which has all the features I need.

Resources