3

I'm trying to show a frame that presents a user with a list of items, which the user can choose from (from a combobox menu) and the user is now able to select ok to choose the selected item, or press cancel, which returns null. These are the default options given to me.

Here's my code snippet:

Set<ICarOption> optionSet = spec.getCarOptions(category);
// If the category is not mandatory, create a makeshift option object to skip.
if(!category.getMandatory())    optionSet.add(new CarOption("skip"));
ICarOption[] optionArray = optionSet.toArray(new ICarOption[optionSet.size()]);

ICarOption selectedOption = (ICarOption)JOptionPane.showInputDialog(
                frame,
                "Choose one of the following options for Category " + category + ".\n" + 
                "If skip is available, you may choose it to skip this category.",
                "Select a feature",
                JOptionPane.QUESTION_MESSAGE, 
                null, 
                optionArray, 
                optionArray[0]);

This code happens in a for loop, iterating over categories, where the category is not always mandatory. This means that if I want to allow a user to have the option to skip a category, I implement another option to the combobox called skip, and if that is selected, I will skip it accordingly. But this feels like a dirty way of doing things (skip is not an option at all in the sense of my defined ICarOption object) and I'd rather have a button called skip that's greyed out (not clickable) if the category is mandatory and available when the category is not mandatory.

I've seen some examples here: http://docs.oracle.com/javase/7/docs/api/javax/swing/JOptionPane.html

It seems to show that I should replace my combobox with a self defined list of buttons, which is not what I want. I need to have 3 buttons (ok, skip, cancel), as well as the list of items.

UPDATE: To illustrate what my GUI would look like:

  1. The frame gets made.
  2. On the dialog window you see a combobox (dropdown list) of ICarOption objects
  3. There are also 3 buttons on the window: OK, SKIP and CANCEL
  4. If the category was mandatory, SKIP will be greyed out.
  5. If OK is selected, the currently selected item in the combobox will be given to the selectedOption variable
  6. If CANCEL is selected, selectedOption == null
  7. If SKIP is selected, this category will be skipped (continue;)

This means that on the input window I need to see a combobox with items and 3 buttons.

-Removed the subquestion-

UPDATE2: I just realized I can't use JButton either because I need to perform quite a few actions within the actionListener, and it requires the variables to be final, some of which cannot be final.

Currently my code looks like this:

JPanel panel = new JPanel();

JComboBox<ICarOption> optionsBox = new JComboBox<ICarOption>();
panel.add(optionsBox);
for(ICarOption option : spec.getCarOptions(category)){
    optionsBox.addItem(option);
}

Object[] options = { "Select option", "Skip", "Cancel" };

int selected = JOptionPane.showOptionDialog(
                panel,
                "Choose one of the following options for Category " + category + ".\n" + 
                "If skip is available, you may choose it to skip this category.",
                "Select option",
                JOptionPane.YES_NO_CANCEL_OPTION, 
                JOptionPane.INFORMATION_MESSAGE, null, 
                options, 
                options[0]);

if(selected == JOptionPane.NO_OPTION)   continue;
if(selected == JOptionPane.CANCEL_OPTION)   throw new UnavailableException();
if(selected == JOptionPane.YES_OPTION){
    ...
}

Inspiration gotten from: Java: Custom Buttons in showInputDialog

The problem with this is that I now don't have a way of controlling the skip button, as it gets created the moment the window gets created.

UPDATE3: It works now, but I'm not proud of how I did it..

        JPanel panel = new JPanel();

        JComboBox<ICarOption> optionsBox = new JComboBox<ICarOption>();
        panel.add(optionsBox);
        for(ICarOption option : spec.getCarOptions(category)){
            optionsBox.addItem(option);
        }

        int selected;
        if(!category.getMandatory()){
            Object[] options = { "Select option", "Cancel", "Skip" };

            selected = JOptionPane.showOptionDialog(
                    panel,
                    "Choose one of the following options for Category " + category + ".\n" + 
                    "If skip is available, you may choose it to skip this category.",
                    "Select option",
                    JOptionPane.YES_NO_CANCEL_OPTION, 
                    JOptionPane.INFORMATION_MESSAGE, null, 
                    options, 
                    options[0]);
        }
        else{
            Object[] options = { "Select option", "Cancel" };

            selected = JOptionPane.showOptionDialog(
                    panel,
                    "Choose one of the following options for Category " + category + ".\n" + 
                    "If skip is available, you may choose it to skip this category.",
                    "Select option",
                    JOptionPane.YES_NO_OPTION, 
                    JOptionPane.INFORMATION_MESSAGE, null, 
                    options, 
                    options[0]);
        }

        // careful! CANCEL_OPTION means skip has been pressed and NO_OPTION means cancel
        if(selected == JOptionPane.CANCEL_OPTION)   continue;
        if(selected == JOptionPane.NO_OPTION)   throw new UnavailableException();
        if(selected == JOptionPane.YES_OPTION){
            ...
        }

There's definitely a chunk of duplicate code there, but this is a way that works and feels better than passing skip as an object.

UPDATE4: Changed the duplicate part to the following:

        ArrayList<Object> tempList = new ArrayList<Object>();
        int optionType;

        tempList.add("Select option");
        tempList.add("Cancel");
        if(!category.getMandatory()){
            tempList.add("Skip");
            optionType = JOptionPane.YES_NO_CANCEL_OPTION;
        }
        else    optionType = JOptionPane.YES_NO_OPTION;

        Object[] options = tempList.toArray(new Object[tempList.size()]);

        int selected = JOptionPane.showOptionDialog(
                panel,
                "Choose one of the following options for Category " + category + ".\n" + 
                        "If skip is available, you may choose it to skip this category.",
                        "Select option",
                        optionType, 
                        JOptionPane.INFORMATION_MESSAGE, null, 
                        options, 
                        options[0]);

So I store the initialization in an ArrayList and convert it to an array afterwards. This is starting to look pretty good to me. :p I have to say I severely underestimated this problem. I simply wanted to change from adding an object to my list of items in order to skip, to having a skip button. That somehow took me several hours to do 'properly'.

small edit (note to user3469755): My apologies, I just looked over some of the previous edits and something just hit me. Your original answer provided me with what I wanted all along... The problem I had with using the listener is that I put a lot of the functionality in the OK-button, but all it really needed to do was to assign the selected item from the dropdown list to the parameter 'selectedOption'. The rest of the functionality I simply had to add after the showOptionDialog and the part where skip and cancel gets handled, as I'm sure that at that point, an item has been selected and the parameter will hold an object. I can be incredibly dense sometimes..

Community
  • 1
  • 1
Babyburger
  • 1,730
  • 3
  • 19
  • 32

2 Answers2

3

The documentation you linked is pretty straight forward:

options: A more detailed description of the set of option buttons that will appear at the bottom of the dialog box. The usual value for the options parameter is an array of Strings. But the parameter type is an array of Objects

You therefore just need to define some JButtons of your choice, wrap them in an array (like your String array) and then pass them onto the JOptionPane.showOptionDialog-method. For interactivity of the JButtons you can use some Mouseclick listeners for example and to grey-out and make it non-clickable (different things!) you can change the JButton-properties with setEnabled();

Here is the full example I wrote:

    JButton jbt_ok = new JButton("OK");
    JButton jbt_skip = new JButton("Skip");
    JButton jbt_cancel = new JButton("Cancel");

    boolean greyOutSkipButton = true;

    jbt_ok.addMouseListener(new MouseAdapter() {

        @Override
        public void mouseClicked(MouseEvent e) {
            System.out.println("OK was clicked");

        }
    });

    jbt_cancel.addMouseListener(new MouseAdapter() {

        @Override
        public void mouseClicked(MouseEvent e) {
            System.out.println("Cancel was clicked");

        }
    });

    if(greyOutSkipButton)
        jbt_skip.setEnabled(false);
    else

        jbt_skip.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("Skip was clicked");

            }
        });

    Object[] options = {jbt_ok, jbt_skip, jbt_cancel};

    JOptionPane.showOptionDialog(null, "Click OK to continue", "Warning",
            JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
            null, options, options[0]);
Killgnom
  • 150
  • 8
  • Thanks! The setEnabled feature is very useful indeed. However, I kind of wanted to avoid defining my own buttons like this (except for skip, which was probably unavoidable). The current OK and CANCEL buttons already do what I want: to return the selected item and return null if cancelled. I simply want to add another button called skip that provides additional functionality. Also, I don't want to use the showOptionDialog; I was merely wondering about how the buttons worked. In the example, strings are given, not buttons. I'd like to know how to add an additional button to showInputDialog. – Babyburger Apr 13 '14 at 13:16
  • I have adjusted the question to better illustrate what I want (and removed the subquestion as it seemed to make my question confusing). – Babyburger Apr 13 '14 at 13:26
  • 1
    Regarding your Update 3: To minimize redundant code you may want to set only the two variables that change in the if and else clause. So in if you set "options" to your three options and optionType to JOptionPane.YES_NO_CANCEL_OPTION and in else clause set them to the the other values. After that you only need to write JOptionPane.showOptionDialog() once and take the parameters you prepared in if/else-clause – Killgnom Apr 13 '14 at 19:01
  • Thank you for still showing interest in this question. I had thought of that, but I couldn't find a way to do it. One of the 2 parameters I'm trying to pass is an array. If I define the array outside the if/else, I cannot initialize it anymore inside the if/else. If I define the array (Object[] o = ...) inside the if/else, then once I'm outside the if/else, it won't get recognized as a variable anymore. EDIT: I just thought of something; I could initialize on an arraylist and convert to an array after the if/else. That might be better, though still not optimal. – Babyburger Apr 13 '14 at 20:27
  • 1
    Last message from me, please don't feel pushed, but I also appreciate good coding style like you seem to ;). Maybe if I post some code you see what I mean. Check out: http://pastecode.org/index.php/view/raw/77467373 – Killgnom Apr 13 '14 at 21:07
  • Why didn't I think of that. :P I'm not sure if it's more 'efficient' to do it with the array (your method) or with an arraylist, but what you wrote is definitely easier to read. I'm going to use the arrays as you suggested. Thanks! – Babyburger Apr 13 '14 at 23:00
2

Main question:

I would add a seperate button, if category is not mandatory and to that button add ActionListener that would skip the category and to go next one.

Subquestion:

You need to get response from user.

Object[] options = { "OK", "CANCEL" };
Object answer = JOptionPane.showOptionDialog(null, "Click OK to continue", "Warning",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
null, options, options[0]);

switch ((String) answer){
case "OK":
    //do stuff
Hans
  • 752
  • 1
  • 15
  • 29
  • Thanks for you answer. The part of how to add the button was confusing me though, but I just found it myself. I was mixing the showInputDialog and the JOptionPane :P showInputDialog is the one defining the list of items (selectionValues) and JOptionPane defines the buttons (options). My reading comprehension needs some work. About the subquestion; having to redefine ok and cancel seems a bit annoying. I like the way they currently work (ok returns the selected item and cancel returns null). I would simply like to add a button called skip to the existing set. Not possible? – Babyburger Apr 13 '14 at 12:58