Is it possible to have per project menus?
Howdy,
I'd like to dynamically create a project menu based on the project contents. Something like:
project one:
menu: File Edit ... Help MyMenu
one
two
project two:
menu: File Edit ... Help MyMenu
three
With menus being controlled by ActionManager, which doesn't use a Project component, so they appear to be global. So using ProjectListener.projectOpened to add my project specific menu results in the last project opened MyMenu being on all project windows.
Any hints or suggestions?
TIA,
Roy
Please sign in to leave a comment.
And the answer is YES.
Basically replace the project menu on gaining focus.
Here's the pattern:
The MyMenu singleton's update(Project) method handles replacing the menu with the project specific actions.
HTH
No, this is wrong, please don't do that!
The correct solution is to register your actions normally, and to hide them by calling presentation.setVisible(false) from your implementation of AnAction.update() when you're not in the right project.
Using AnAction.update almost works. What happens is AnAction.update is called after the menu is displayed or sometimes on mouse over. So which menu items are visible gets garbled between projects.
If I use the windowsGainedFocus to send an update event to all the actions attached to the menu then behavior is correct.
public void windowGainedFocus(WindowEvent e) {
MyMenu.getInstance().update();
}
class MyMenu {
public void update() {
ActionManager am = ActionManager.getInstance();
DefaultActionGroup mainMenu = (DefaultActionGroup) am.getAction("MainMenu");
DefaultActionGroup myMenu = findChild(mainMenu, "MyMenu");
updateMenu(myMenu);
}
private void updateMenu(DefaultActionGroup menu) {
if (menu != null) {
for(AnAction action : menu.getChildActionsOrStubs()) {
DataContext dataContext = DataManager.getInstance().getDataContext();
action.update(AnActionEvent.createFromAnAction(action, null, ActionPlaces.MAIN_MENU, dataContext));
if (action instanceof DefaultActionGroup) {
updateMenu((DefaultActionGroup) action);
}
}
}
}
//...
}
A secondary issue is presentation.setVisible(false) just grays out the menu item (to me, grayed out should mean disabled while setVisible(false) should make the menu item invisible.
Thank you
Why are you doing this at all? IntelliJ IDEA will call the update() method of an action by itself, at the correct time. You don't need any code to call the update() method manually.
Because AnAction.update is not being invoked enough. Try this, put a Log.info in your AnAction.update handlers. Run the plugin from within the idea. Open a project. So far no updates. Open menu with instrumented actions. No updates. Note that the menu is drawn and there have been zero calls to update. Move mouse down menu and watch update be invoked on mouse over.
Second test. Open another project. No updates. Open menu. No updates. Mouse over gets updates. Change focus to first project. No updates. Open menu, no updates, and the state is partially from other project.
So to have dynamic per project menus, the update event needs to be generated for the actions prior to the user opening the menu. Ideally this would be on a project activate event. Doing it on focus gained for the project's window is the closest I have found to project activation.
Thank you.
If this is true, then it's a bug that needs to be fixed on our side. Opening a menu is supposed to call update() for all the actions in the menu and to hide the ones for which setVisible(false) was called.