Ahamed
Ahamed

Reputation: 39695

JOptionPane to get password

JOptionPane can be used to get string inputs from user, but in my case, I want to display a password field in showInputDialog.

The way I need is the input given by the user should be masked and the return value must be in char[]. I need a dialog box with a message, password field, and two buttons. Can that be done? Thanks.

Upvotes: 55

Views: 72590

Answers (7)

Eng.Fouad
Eng.Fouad

Reputation: 117665

Yes, it is possible using JOptionPane.showOptionDialog(). Something like this:

JPanel panel = new JPanel();
JLabel label = new JLabel("Enter a password:");
JPasswordField pass = new JPasswordField(10);
panel.add(label);
panel.add(pass);
String[] options = new String[]{"OK", "Cancel"};
int option = JOptionPane.showOptionDialog(null, panel, "The title",
                         JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE,
                         null, options, options[0]);
if(option == 0) // pressing OK button
{
    char[] password = pass.getPassword();
    System.out.println("Your password is: " + new String(password));
}

Upvotes: 77

acabra85
acabra85

Reputation: 369

An attempt to simplify the code after the original solution from @Adamski and @Peter West.

All the wiring is done in the PasswordPanel class and the client only calls the static getPassword method.

String passwordText = PasswordPanel.getPassword();
if (null != passwordText) {
    System.out.println("password: <" + passwordText + ">");
}

source:

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class PasswordPanel extends JPanel {
    private final String password;

    private PasswordPanel(String prompt) {
        super(new FlowLayout());
        JPasswordField pwdField = new JPasswordField(12);
        add(pwdField);
        JOptionPane joptionPane = new JOptionPane(this, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
        boolean responseOK = configure(joptionPane, prompt, pwdField).equals(JOptionPane.OK_OPTION);
        this.password = responseOK ? String.valueOf(pwdField.getPassword()) : null;
    }

    public static String getPassword(){
        return new PasswordPanel("Input Password ... ").password;
    }

    private Object configure(JOptionPane jOptionPane, String prompt, JComponent pwdField) {
        JDialog jDialog = promptDialog(prompt, jOptionPane, pwdField);
        Object result = jOptionPane.getValue();
        jDialog.dispatchEvent(new WindowEvent(jDialog, WindowEvent.WINDOW_CLOSING));
        jDialog.dispose();
        return result;
    }

    private JDialog promptDialog(String message, JOptionPane jOptionPane, JComponent pwdField) {
        JDialog dialog = jOptionPane.createDialog(message);
        dialog.addWindowFocusListener(new WindowAdapter() {
            @Override
            public void windowGainedFocus(WindowEvent e) {
                pwdField.requestFocusInWindow();
            }
        });
        dialog.setVisible(true);
        return dialog;
    }
}

Upvotes: 1

Love
Love

Reputation: 1769

A Kotlin solution based on Adamski's answer.

This time without buttons that takes focus, just enter password and press Enter, or Escape to discard input:

class PasswordPanel : JPanel(FlowLayout()) {

    private val passwordField = JPasswordField(20)
    private var entered = false

    val enteredPassword
        get() = if (entered) String(passwordField.password) else ""

    init {
        add(JLabel("Password: "))
        add(passwordField)
        passwordField.setActionCommand("OK")
        passwordField.addActionListener {
            if (it.actionCommand == "OK") {
                entered = true

                // https://stackoverflow.com/a/51356151/1020871
                SwingUtilities.getWindowAncestor(it.source as JComponent)
                    .dispose()
            }
        }
    }

    private fun request(passwordIdentifier: String) = apply {
        JOptionPane.showOptionDialog(null, this@PasswordPanel,
            "Enter $passwordIdentifier",
            JOptionPane.DEFAULT_OPTION,
            JOptionPane.INFORMATION_MESSAGE,
            null, emptyArray(), null)
    }

    companion object {

        fun requestPassword(passwordIdentifier: String) = PasswordPanel()
            .request(passwordIdentifier)
            .enteredPassword
    }
}

Usage: PasswordPanel.requestPassword(passwordIdentifier)

Upvotes: 1

Agi Hammerthief
Agi Hammerthief

Reputation: 2134

The following class is an extension of Adamski's great answer:

package com.stackoverflow.swing;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;

/**
 * Class that creates a panel with a password field. Extension of Adamski's class
 *
 * @author adamski https://stackoverflow.com/users/127479/adamski
 * @author agi-hammerthief https://stackoverflow.com/users/2225787/agi-hammerthief
 * @see https://stackoverflow.com/a/8881370/2225787
 */
public class PasswordPanel extends JPanel {

  private final JPasswordField JFieldPass;
  private JLabel JLblPass;
  private boolean gainedFocusBefore;

  /**
   * "Hook" method that causes the JPasswordField to request focus when method is
   * first  called.
   */
  public void gainedFocus () {
    if (!gainedFocusBefore) {
      gainedFocusBefore = true;
      JFieldPass.requestFocusInWindow();
    }
  }

  public PasswordPanel (int length) {
    super(new FlowLayout());
    gainedFocusBefore = false;
    JFieldPass = new JPasswordField(length);
    Dimension d = new Dimension();
    d.setSize(30, 22);
    JFieldPass.setMinimumSize(d);
    JFieldPass.setColumns(10);
    JLblPass = new JLabel("Password: ");
    add(JLblPass);
    add(JFieldPass);
  }

  public PasswordPanel() {
    super(new FlowLayout());
    gainedFocusBefore = false;
    JFieldPass = new JPasswordField();
    Dimension d = new Dimension();
    d.setSize(30, 22);
    JFieldPass.setMinimumSize(d);
    JFieldPass.setColumns(10);
    JLblPass = new JLabel("Password: ");
    add(JLblPass);
    add(JFieldPass);
  }

  public char[] getPassword() {
    return JFieldPass.getPassword();
  }

  public String getPasswordString() {
    StringBuilder passBuilder = new StringBuilder();

    char[] pwd = this.getPassword();
    if (pwd.length > 0) {
      for (char c : pwd) {
        passBuilder.append(c);
      }
    }

    return passBuilder.toString();
  }

  private static String displayDialog (
    Component parent, final PasswordPanel panel, String title
  ) {
    String password = null;
    /* For some reason, using `JOptionPane(panel, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE)`
    does not give the same results as setting values after creation, which is weird */
    JOptionPane op = new JOptionPane(panel);
    op.setMessageType(JOptionPane.QUESTION_MESSAGE);
    op.setOptionType(JOptionPane.OK_CANCEL_OPTION);
    JDialog dlg = op.createDialog(parent, title);
    // Ensure the JPasswordField is able to request focus when the dialog is first shown.
    dlg.addWindowFocusListener (new WindowAdapter () {
      @Override
      public void windowGainedFocus (WindowEvent e) {
        panel.gainedFocus ();
      }
    });
    dlg.setDefaultCloseOperation (JOptionPane.OK_OPTION); // necessary?

    dlg.setVisible (true);

    Object val = op.getValue ();
    if (null != val && val.equals (JOptionPane.OK_OPTION)) {
      password = panel.getPasswordString();
    }

    return password;
  }

  public static String showDialog (Component parent, String title) {
    final PasswordPanel pPnl = new PasswordPanel();
    return displayDialog(parent, pPnl, title);
  }

  public static String showDialog (
    Component parent, String title, int passwordLength
  ) {
    final PasswordPanel pPnl = new PasswordPanel(passwordLength);

    return displayDialog (parent, pPnl, title);
  }

  public static String showDialog (String title) {
    return showDialog(null, title);
  }

  public static String showDialog (String title, int passwordLength) {
    return showDialog(null, title, passwordLength);
  }

  /**
   * Show a test dialog
   */
  public static void main(String[] args) {
    String test = PasswordPanel.showDialog ("Enter Your Password");
    System.out.println ("Entered Password: " + test);
    System.exit(0);
  }

}

Upvotes: 0

Peter West
Peter West

Reputation: 31

This dialogue looks a lot better if you do

dlg.setVisible(true);

Without that you can't see it at all.

Also

pPnl.gainedFocus();

should be

pPnl.gainedFocus();

Other than that it works great. Thanks for the code. Saved me time facing around with Swing.

Also, if you don't want to leave the dialogue running in the background every time you open it, you'll need to close it with something like

dlg.dispatchEvent(new WindowEvent(dlg, WindowEvent.WINDOW_CLOSING));
dlg.dispose(); // else java VM will wait for dialog to be disposed of (forever)

Upvotes: 3

Adamski
Adamski

Reputation: 54725

The easiest thing is to use JOptionPane's showConfirmDialog method and to pass in a reference to a JPasswordField; e.g.

JPasswordField pf = new JPasswordField();
int okCxl = JOptionPane.showConfirmDialog(null, pf, "Enter Password", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);

if (okCxl == JOptionPane.OK_OPTION) {
  String password = new String(pf.getPassword());
  System.err.println("You entered: " + password);
}

Edit

Below is an example using a custom JPanel to display a message along with the JPasswordField. Per the most recent comment, I've also (hastily) added code to allow the JPasswordField to gain focus when the dialog is first displayed.

public class PasswordPanel extends JPanel {
  private final JPasswordField passwordField = new JPasswordField(12);
  private boolean gainedFocusBefore;

  /**
   * "Hook" method that causes the JPasswordField to request focus the first time this method is called.
   */
  void gainedFocus() {
    if (!gainedFocusBefore) {
      gainedFocusBefore = true;
      passwordField.requestFocusInWindow();
    }
  }

  public PasswordPanel() {
    super(new FlowLayout());

    add(new JLabel("Password: "));
    add(passwordField);
  }

  public char[] getPassword() {
      return passwordField.getPassword();
  }

  public static void main(String[] args) {
      PasswordPanel pPnl = new PasswordPanel();
      JOptionPane op = new JOptionPane(pPnl, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);

      JDialog dlg = op.createDialog("Who Goes There?");

      // Wire up FocusListener to ensure JPasswordField is able to request focus when the dialog is first shown.
      dlg.addWindowFocusListener(new WindowAdapter() {
        @Override
        public void windowGainedFocus(WindowEvent e) {
            pPnl.gainedFocus();
        }
      });

      if (op.getValue() != null && op.getValue().equals(JOptionPane.OK_OPTION)) {
          String password = new String(pPnl.getPassword());
          System.err.println("You entered: " + password);
      }
  }
}

Upvotes: 49

OnResolve
OnResolve

Reputation: 4032

You can create you're own dialog that extends JDialog and then you can put whatever you want in it.

Upvotes: 4

Related Questions