0

To be clear, the several similar-appearing entries here DO NOT actually talk about building a menu dynamically since all their object name choices and such are already in their code as fixed strings already written in the source code; all they're doing is waiting until run-time to create their statically designed menu items. Here are two entries I found like that: One and Two. The concerns there merely had to do with the trivial (but vital) task of refreshing the display, NOT with anything like actual dynamic creation of content.

What I want to do, in sharp contrast, is to truly add dynamically: I want the user to be able to choose to add items to a sub-menu that they can then later select and have take action within the application.

Let's take the case of simply adding an integer value to a menu and then being able to select it later, similar to what can easily be done with a combo-box but instead done with a menu.

The problem isn't the syntax pertaining to defining, for example, a MenuListener that will point to a method that knows how to act, that's not the problem. Rather, I just don't know enough about the dynamic NAMING SPACE, and how to "de-reference" a String, for example, as an object name. Bluntly, how do I dynamically name my new objects that I didn't anticipate creating (not in kind but in number)? IOW, how do I take a cleverly constructed string that actually contains code I want run and then ask Java to run it? What's the Java syntax for that? Maybe the problem can be reduced to just object names; Say, the name comes as a string I can construct; how do use that in my JMenuItem declaration? ...I know how to do this in BASH, but how is this done in Java?

(I'm hoping I don't have to create it as a file, compile it, and somehow attach the class file(s) to my running program and then run it - DAMN that would be cumbersome!)

Thanks.

Richard T
  • 4,570
  • 5
  • 37
  • 49
  • I'd like to see a pseudo example of this. You already know the "kind" but not the number but what does that have to do with accepting a string that contains Java? code? – ChiefTwoPencils Jul 14 '18 at 00:23
  • Dynamically creating a menu isn't that different from creating a static menu, it's just the properties become variables instead of been hardcoded. The real question is how would you execute a command, given that most of the information is textual – MadProgrammer Jul 14 '18 at 01:01
  • *"how do I take a cleverly constructed string that actually contains code I want run and then ask Java to run it? What's the Java syntax for that?"* - What does this *"cleverly constructed string"* actually look like? Is it JavaScript, Java code, your own invention? – MadProgrammer Jul 14 '18 at 01:03
  • @MadProgrammer You say "isn't that different from creating a static menu". Fine, then how do I specify the name to which I apply constructs like instantiation? Can you give an example syntax for "JMenuItem = new JMenuItem();" Or ".setFont(myFont);" ... as some simple examples? If so, use that to create an answer for my question! – Richard T Jul 14 '18 at 01:42
  • @RichardT Use a `HashMap` or `List` if you don't want to maintain a single variable reference – MadProgrammer Jul 14 '18 at 01:44
  • @ChiefTwoPencils ...If I knew how to mock it up in pseudo-code, I'd probably not have had to ask the question in the first place, presuming it's even possible. However, since you asked, please see my reply to MadProgrammer just above. That might be defined as "String NewObjName = "NewMenuItem"+i; The question then is how to get NewObjName to be used in a construct like "JMenuItem NewObjName = new JMenuItem();" and have the CONTENTS of NewObjName be used for the name of the new object that statement would instantiate. (This is called "dereferencing".) – Richard T Jul 14 '18 at 01:58

1 Answers1

1

If I understand your overall intent, then I would recommend starting with the Actions API which be used to create independent units of work which are independent of how they are displayed.

This allows you to define re-usable (or in your case, dynamic) operations, which can be executed via menus, toolbars, buttons and even key bindings out of the box.

Because setting up a Action can be a little tedious, I might consider using a builder pattern, but you don't have to, you can build them manually if you wish ;)

public class ActionBuilder {
    private ActioBuilderAction action;

    public ActionBuilder() {
        action = new ActionBuilder.ActioBuilderAction();
    }

    public ActionBuilder toolTip(String text) {
        action.putValue(Action.SHORT_DESCRIPTION, text);
        return this;
    }

    public ActionBuilder command(String text) {
        action.putValue(Action.ACTION_COMMAND_KEY, text);
        return this;
    }

    public ActionBuilder mnemonic(int key) {
        action.putValue(Action.MNEMONIC_KEY, key);
        return this;
    }

    public ActionBuilder displayedMnemonicIndex(int index) {
        action.putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, index);
        return this;
    }

    public ActionBuilder text(String text) {
        action.putValue(Action.NAME, text);
        return this;
    }

    public ActionBuilder smallIcon(Icon icon) {
        action.putValue(Action.SMALL_ICON, icon);
        return this;
    }

    public ActionBuilder largeIcon(Icon icon) {
        action.putValue(Action.LARGE_ICON_KEY, icon);
        return this;
    }

    public ActionBuilder acceleratorKey(KeyStroke ks) {
        action.putValue(Action.ACCELERATOR_KEY, ks);
        return this;
    }

    public ActionBuilder actionListener(ActionListener listener) {
        action.setListener(listener);
    }

    public Action build() {
        return action;
    }

    public class ActioBuilderAction extends AbstractAction {

        private ActionListener listener;

        public void setListener(ActionListener listener) {
            this.listener = listener;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (listener != null) {
                listener.actionPerformed(e);
            }
        }

    }
}

Then, you could simply build a new menu something like...

Action action = new ActionBuilder().text("Super awesome command").actionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Super aweseom comand GO!");
    }
}).build();

JMenuItem mi = new JMenuItem(action);

Now, I imagine, you might have a "command executor" class of some kind, which would. physically execute the command. I'd create a bridging class which implemented ActionListener and when it's called, would then execute the specified command

public class CommandListener implements ActionListener {
    private String command;

    public CommandListener(String command) {
        this.command = command;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        CommandExecutor executor = new CommandExecutor();
        executor.execute(command)
    }


}

This could then be used in place of the ActionListener in the first example...

Action action = new ActionBuilder().text(commandName).actionListener(new CommandListener(command)).build();

As an overall idea

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • This looks creative and while perhaps a little cumbersome, much less so than writing code to a file to be compiled, etc... (which I think is doable but not easily!) ... So, this deserves a lot more research on my part - I've just been ignorant of the "Actions API" so far! ... BTW, I didn't understand your comment above about "maintain a single variable reference". ... There are many ways to keep track of the names, it's the "de-reference" part, where a variable's contents are treated like code that's the big trick (and, I think, a big hole in Java's design). – Richard T Jul 14 '18 at 01:50
  • @RichardT Yes and no. Part of the problem is needing to "compile" the code to byte code, as well as API references and other possible issues. Java does allow you to load classes at runtime via custom class loading or even [compile and load it](https://stackoverflow.com/questions/21544446/how-do-you-dynamically-compile-and-load-external-java-classes/21544850#21544850), while it's certainly not "pretty', it wouldn't be hard to build a wrapper API around it. – MadProgrammer Jul 14 '18 at 01:58
  • @RichardT For work which doesn't need the Java libraries, you can also execute [JavaScript](https://stackoverflow.com/questions/14320308/how-to-run-javascript-in-java-programming) – MadProgrammer Jul 14 '18 at 01:59
  • Yes, I understand the byte-code problem, but a more sensible design of Java would have anticipated this need and accommodated dynamic byte-code creation into the present context as a core part of the architecture. It's certainly doable, but not by "mere mortals." (I'm sure I could do it if I had a life to devote to the task - or had I been on the original dev team.) ... Yes, I've done some of that API wrapping in the past, you're right, but still a lot of work. Finally, it needs to work in the context of the already running code - full Java. – Richard T Jul 14 '18 at 02:05
  • Me personally, I'd use a plugin in style framework, where units of pre-compiled code can be loaded into the application at runtime and executed, but that's just me – MadProgrammer Jul 14 '18 at 02:56