I am working on 3rd-party plugin for a desktop Windows app. It is basically a traditional DLL that is only ever accessed by the host application. The host provides menu items for the plugin, based on an API, but I would like to extend the API by modifying the text of my menu items dynamically based on which file is open. The host uses an MDI window and I think it is developed with MFC. However, I am attempting to change the menu items directly with the Win API. (This may be the problem right there, which is part of my question.)
The code works correctly the first time I change the menu item. But subsequent changes do not appear on the menu. The weird thing is that GetMenuString seems always to return the value I set it to. That means that on the API level it appears to work, but the menu items do not change (except for the first time).
Here is the code I use to change the menu item text. It's very basic.
MENUITEMINFOW menuInfo;
memset ( &menuInfo, 0, sizeof(menuInfo) );
menuInfo.cbSize = sizeof(menuInfo);
menuInfo.fMask = MIIM_STRING;
menuInfo.fType = MFT_STRING;
menuInfo.dwTypeData = (LPWSTR)newItemText;
SetMenuItemInfoW (hMenu, idToChange, false, &menuInfo);
Could MFC be interfering with this? Or perhaps an idiosyncrasy of the host app's menu handling? Or is there something else I need to do to get the menu to display correctly?
I got this to work by using DeleteMenu and InsertMenu instead of SetMenuItemInfo. The code is straigthforward, but here it is in case it helps someone else like me.
const UINT idToChange = GetMenuItemID ( hMenu, index );
DeleteMenu ( hMenu, index, MF_BYPOSITION );
InsertMenuW ( hMenu, index, MF_BYPOSITION | MF_STRING, idToChange, (LPWSTR)newItemText );
Related
I'm wondering how to add several UI panels to a scripting palette in GMS 2.x. I found some commands in Gatan's outdated documentation (http://digitalmicrograph-scripting.tavernmaker.de/other%20resources/Old-DMHelp/FloatingPalettes.htm) but most of them are not found in GMS 2.32.
Given a UI class "UI_class" (including an init() function that creates the interface) I've already successfully registered the script palette and opened the corresponding gadget using:
object UI=Alloc(UI_class).init()
RegisterScriptPalette(UI,"Type","Display")
OpenGadgetPanel("Display")
Unfortunately, the command GadgetWindowAddGadgetPanel() which seemed to be the most promising to me in the first instance in order to add a second UI to the same palette cannot be found.
Does anybody know a working equivalent?
Thank you in advance for sharing your experience, I'm looking forward to see some nicely designed floating palettes!
I'm not 100% sure I understood the question correctly. Each UI derived object forms it's own "dialog", which can be displayed either as
Modal Dialog - using Pose( DlgObj )
Modeless Dialog - using Display( DlgObj, "name" )
Gadget panel - by first 'registering' it and then displaying a registed dialog
If you have multiple gadget of same width, you can add them together by drag-and drop. This is not part of the dialog - just part of how the UI interaction with dialogs. ( You drag a palette to the side of the screen to attach it, and then you can dragg other palettes on top of it. )
|
|
Example script to produce some dialogs:
Object CreateDlgObj()
{
TagGroup DialogTG = DLGCreateDialog("My Dialog")
TagGroup StrFieldTG = DLGCreateStringField("Some string",20)
DialogTG.DLGAddElement(StrFieldTG)
Object DialogObj = Alloc(UIFrame)
DialogOBJ.Init(DialogTG)
return DialogObj
}
// MAIN
// Show dialog as modal dialog
// CreateDLGObj().Pose()
// Show dialog as modeless dialog
// CreateDLGObj().Display( "NewName" )
// Register dialog as gadget and then display
// RegisterScriptPalette( CreateDLGObj(),"", "RegDlg" )
// OpenGadgetPanel( "RegDlg" )
// Create, register and display multiple dialogs as palettes
number nMax = 2
For( number i =0; i<nMax; i++)
{
string name = "MyDlg_" + i
RegisterScriptPalette( CreateDLGObj(),"", name )
OpenGadgetPanel( name )
}
Note that "Gadget panels" or "tool palettes" or whatever you call them are different in GMS 1.x, GMS 2.x and GMS 3.x as it is always the "main program" which decides how to handle the dialogs. GMS 2.x is the only version, where the program "remembers" the layout of multiple such palettes. These layouts can be saved and loaded. GMS 3 no longer has this, as all UI palettes are fixed in position. (Old custom dialogs still appear, but in a somewhat 'out-of-style' way.
GMS 2.x's "Floating Window Layout" tool:
The "layout" information stored with this tool is actually stored in the Windows registry.
But I have never used that so far. (And it also only applies to GMS 2.x.)
It is also worth mentioning, that RegisterScriptPalette does exactly what is says: It adds the UI permanently to the list of palettes the appliction knows of.
So running the script twice, you get two identical palettes. You usually don't want that! Just use OpenGadgetPanel to show any already registered palette.
However, 'registered' palettes will only remain 'registered' as long as the application is running, so they are gone at restart of DM.
If you want somethng more persistent, you need to install the code as a Library via the File menu. Note, that any executeable part of the code will be automatically run on startup. So you want to have the RegisterScriptPalette be part of that code, but not the OpenGadgetPanel.
I created menus and sub menus using the resource editor in visual studio. I now want to add items to one of the menus during run time. I was going to use the InsertMenuItem function but I don't know how to get access to the HMENU variable.
LoadMenu seems is what you need. Use it to load menu from resource editor, something like this:
HMENU yourMenu = LoadMenu( hInst, // variable where you stored your HINSTANCE
MAKEINTRESOURCE(IDM_MENU1) ); // replace IDM_MENU1 with the ID of your menu
Here are lots of useful examples, you may find very useful. Some of them address your issue, and some might be useful to you in the future. I would study the Example of Menu-Item Bitmaps section if I were you...
If you need a menu handle that is already assigned to a window then use GetMenu as member arx said. Something like this:
HMENU yourMenu = GetMenu(hWnd); // hWnd is the HWND of the window that owns your menu
Do not forget to destroy the menu when it is no longer needed ( usually upon window destruction ) with DestroyMenu.
This example might help you as well. This is very good introductory tutorial for Win32, I suggest you to read it ( just go to the home page and download both PDF and .zip file with code examples ).
As I have said before, your question is not entirely clear, so if you have further questions leave me a comment.
Hopefully this answer solved your problems. Best regards.
In my MFC dialog based application I have main menu created with Visual Studio resource editor, and in one place there is dynamic menu, created at run time. All parts of menu that are created with Visual studio have modern look, and my dynamically created sub-menu has old look, as shown in image below.
My code for creating dynamic sub-menu is something like this (not the real code, but real code is not all that important):
CMenu subMenu;
subMenu.CreateMenu();
for (...)
{
subMenu.AppendMenu(
MF_STRING | (isChecked ? MF_CHECKED : MF_UNCHECKED),
<some menu ID>,
<some menu text>);
}
Inserting this sub-menu to where it belongs is done like this (pretty much the actual code):
TCHAR szMenuString[256];
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS |
MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
mii.dwTypeData = szMenuString;
mii.cch = sizeof(szMenuString) / sizeof(szMenuString[0]);
GetMenu()->GetMenuItemInfo(ID_SUBMENU, &mii);
mii.fMask |= MIIM_SUBMENU;
mii.hSubMenu = subMenu.GetSafeHmenu();
GetMenu()->SetMenuItemInfo(ID_SUBMENU, &mii);
How can I create my sub-menu so that it appears the same in style as the rest of the main menu?
My code is written in MFC, but your answer does not have to be in MFC (and probably cannot be).
I discovered the solution, but don't really understand what was happening. The solution is to prevent calling DestroyMenu on subMenu's destructor at the end of the function. This is done by either calling subMenu.Detach(), or making subMenu a pointer to CMenu.
What I don't understand is why is DestroyMenu turning new style menu to old style. I would expect that the menu is either destroyed and not shown, or copied in SetMenuItemInfo and so its style preserved. Whoever provides an answer to this one gets my vote :)
Also, I would like to know if I'm producing a resource leak by calling Detach here, or is my dynamic sub-menu destroyed along with the main menu. Points await the one who provides an answer.
I want to make a custom message box. What I want to customize is the button's text.
MessageBoxW(
NULL,
L"Target folder already exists. Do you want to overwrite the folder?",
L"No title",
MB_YESNOCANCEL | MB_ICONQUESTION
);
I'd like to just change the buttons text to Overwrite, Skip, Cancel.
What's the most simple way?
I have to make this as having same look and feel with Windows default messagebox.
As said by others, a typical way is to create a dialog resource and have a completely independent dialog, which GUI you need to design in the way that it looks like standard dialog (to meet your request for feel and look). If you want to accept text messages, you might probably need to add code which resizes the window appropriately.
Still, there is another option for those who feel like diving into advanced things. While MessageBox API does not offer much for fint tuning, you still have SetWindowsHookEx in your hands. Having registgered the hook, you can intercept standard MessageBox window procedure and subclass it in the way you like.
Typical things include:
changing button text
adding more controls
adding timed automatic close
Hooking standard window can do all of those.
UPD. Hey, I realized I have some code with SetWindowsHookEx to share: http://alax.info/blog/127
You could create an own dialog. Or you could use a window hook as described in this article.
An archived version of the article can be found on web.archive.com.
Make a dialog resource (with a GUI editor, or by hand) and call DialogBox on it. There's no way to alter MessageBox behaviour, other than what's supported by its arguments.
That said, your message box can very well use stock Yes/No options.
The task dialog functionality introduced in Vista does exactly what you want and follows the prevailing system theme. However, if you have to support XP, then this will be of little comfort to you.
I know this question is old, but I just stumbled upon it now.
I would like to expand the other answers in regards to using a TaskDialog instead of a MessageBox. Here's a concise example of using a TaskDialog to do precisely what was asked; change the button's texts:
const TASKDIALOG_BUTTON buttons[] = { {IDYES, L"Overwrite"}, {IDNO, L"Skip"}, {IDCANCEL, L"Cancel"} };
TASKDIALOGCONFIG taskDialogConfig = {
.cbSize = sizeof(TASKDIALOGCONFIG),
.pszMainIcon = TD_WARNING_ICON, // TaskDialog does not support a question icon; see below
.pButtons = buttons,
.cButtons = ARRAYSIZE(buttons),
.pszWindowTitle = L"No title",
.pszContent = L"Target folder already exists. Do you want to overwrite the folder?"
};
TaskDialogIndirect(&taskDialogConfig, NULL, NULL, NULL);
Some noteworthy things:
You need to use TaskDialogIndirect, not the basic TaskDialog function
when not specifying a parent window, the icon specified in pszMainIcon is displayed in the title bar as well
There is no equivalent to the MessageBox's MB_ICONQUESTION, quoting a quote from this forumpost: Don't use the question mark icon to ask questions. Again, use the question mark icon only for Help entry points. There is no need to ask questions using the question mark icon anyway—it's sufficient to present a main instruction as a question.
checking which button was selected would have to be done by passing a pointer to an int as the second argument of TaskDialogIndirect and checking its value on return (the documentation should be pretty clear)
Here is a small open source library that allows you to customize Message Boxes. Developed by Hans Ditrich.
I have successfully used it in another POC that allows embedding a custom icon in such MessageBox that can be called even from a Console application.
I should also point to the Task Dialog. Here is an example of using it:
int nButtonPressed = 0;
TaskDialog(NULL, hInst,
MAKEINTRESOURCE(IDS_APPLICATION_TITLE),
MAKEINTRESOURCE(IDS_DOSOMETHING),
MAKEINTRESOURCE(IDS_SOMECONTENT),
TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON,
TD_WARNING_ICON,
&nButtonPressed);
if (IDOK == nButtonPressed)
{
// OK button pressed
}
else if (IDCANCEL == nButtonPressed)
{
// Cancel pressed
}
Is there a way to determine if a menu is dropped down in the win32 api? Something that could be used like so:
HMENU hMenu = GetMenu(hWnd);
HMENU hSubMenu = GetSubMenu(hMenu);
// Is hSubMenu dropped down?
I'm not sure of a way to operate specifically on HMENUs to see if the menu is showing (and a quick scan of the platform SDK docs didn't turn up anything specific), but you might be able to use the GetMenuItemInfo function to get a MENUITEMINFO struct relating to the menu item which owns the dropdown. If the fState member has MFS_HILITE set, that should indicate that the item is selected and the sub menu is most likely open. Correctness isn't guaranteed on my part but it's worth experimenting with.
Another possible option would be using FindWindow with the class "#32768" to find the hWnd of whatever menus may be open, and sending the MN_GETHMENU message to whichever windows you find to retrieve the HMENU and compare it to the expected value from GetSubMenu.