Package com.starlight.ui.login

Source Code of com.starlight.ui.login.LoginFrame$LoginAttempt

/*
* Copyright(c) 2002-2004, StarLight Systems
* All rights reserved.
*/
package com.starlight.ui.login;

import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.starlight.StringKit;
import com.starlight.ui.*;
import com.starlight.ui.validity.ChangeLabelAction;
import com.starlight.ui.validity.DisableComponentAction;
import com.starlight.ui.validity.EnableComponentAction;
import com.starlight.ui.validity.FieldValidityChecker;
import com.starlight.ui.validity.checks.HasSelectionValidityCheck;
import com.starlight.ui.validity.checks.HasTextValidityCheck;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.JTextComponent;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.prefs.Preferences;


/**
* A modal login dialog.
*
* @author reden
*/
@SuppressWarnings( { "UnusedDeclaration" } )
public class LoginFrame<T> extends JFrame {
  private static final String LABEL_KEY = "LABEL";
  private static final String SPINNER_KEY = "SPINNER";

  private static final int MIN_WIDTH = 250;
  private static final int MAX_WIDTH = 700;
  private static final int MIN_HEIGHT = 300;
  private static final int FIELD_COLUMNS = 13;

  private LoginAuthenticator<T> authenticator;
  private String username;
  private T token = null;
  private LoginException error = null;
  private boolean canceled = false;
  private boolean cancel_or_quit;

  private final DefaultFormBuilder component_builder = new DefaultFormBuilder(
    new FormLayout( "fill:pref, 6px, fill:pref, 0px:grow" ) );
  private JLabel user_label;
  private JLabel password_label;
  private JTextField user_field;
  private JPasswordField password_field;

  private JButton ok_button;
  private JButton cancel_button;

  private FieldValidityChecker validator;
  private ValueChangeRelay<JComponent> vcr;

  private JLabel dev_mode_label;
  private JCheckBox dev_mode_check;

  private Thread login_thread = null;

  private CardLayout card_layout = new CardLayout();
  private JPanel card_panel = new JPanel( card_layout );
  private WaitSpinner2 spinner = new WaitSpinner2( false );
  private JLabel label = new JLabel();

  private java.util.List<Option> options = new ArrayList<Option>();
  private java.util.List<JComponent> components_disabled_normally =
    new ArrayList<JComponent>();
  private java.util.List<JComponent> components_disabled_during_login =
    new ArrayList<JComponent>();

  private final Preferences pref_node;

  private Runnable close_handler = null;


  /**
   * Create the dialog.
   *
   * @param authenticator    The authentication driver.
   * @param username      Default username.
   * @param icon        Icon to display (or null).
   * @param version_string  Version string to display (or null). Note, this is
   *               displayed as-is (i.e., "Version:" is not added to the
   *               front of it.
   * @param cancel      Whether the decline button should be labeled "Cancel"
   *               (true) or "Quit" (false).
   * @param title        The title for the frame.
   */
  public LoginFrame( LoginAuthenticator<T> authenticator, String username, Icon icon,
    String version_string, boolean cancel, String title ) {

    super( title );

    this.authenticator = authenticator;
    this.username = username;
    this.cancel_or_quit = cancel;

    if ( authenticator == null ) throw new IllegalArgumentException(
      "Authenticator cannot be null" );

    pref_node = Preferences.userRoot().node( "starlight" ).node(
      authenticator.getClass().getSimpleName() );

    init( icon != null ? new JLabel( icon ) : null, version_string );
  }


  /**
   * Create the dialog.
   *
   * @param authenticator    The authentication driver.
   * @param username      Default username.
   * @param header_component  Component placed in the header of the dialog.
   * @param version_string  Version string to display (or null). Note, this is
   *               displayed as-is (i.e., "Version:" is not added to the
   *               front of it.
   * @param cancel      Whether the decline button should be labeled "Cancel"
   *               (true) or "Quit" (false).
   * @param title        The title for the frame.
   */
  public LoginFrame( LoginAuthenticator<T> authenticator, String username,
    JComponent header_component, String version_string, boolean cancel,
    String title ) {

    super( title );

    this.authenticator = authenticator;
    this.username = username;
    this.cancel_or_quit = cancel;

    if ( authenticator == null ) throw new IllegalArgumentException(
      "Authenticator cannot be null" );

    pref_node = Preferences.userRoot().node( "starlight" ).node(
      authenticator.getClass().getSimpleName() );

    init( header_component, version_string );
  }


  /**
   * Add an option to the information provided in the login dialog.
   *
   * @param option_name    The name of the option, this will be given as the key
   *               in the options map.
   * @param label        The String that will be shown on the label.
   * @param choices      The default choices for the option. Providing null
   *               will make a text field appear. Providing more will cause
   *               a combo box to be used.
   * @param can_edit_choices  Determines whether the user can edit the choices or must
   *               use one provided. Only really makes sense with a combo box.
   * @param require_entry    Determines whether or not something must be entered in the
   *               field in order to attempt login.
   * @param default_value    If non-null, this will be selected.
   */
  public void addOption( final String option_name, String label, String[] choices,
    boolean can_edit_choices, boolean require_entry, String default_value ) {

    if ( choices == null ) {
      addOption( option_name, label, default_value, require_entry );
      return;
    }

    JComboBox<String> box = new JComboBox<String>( choices );
    box.setName( option_name );
    box.setEditable( can_edit_choices );

    String initial_selection =
      pref_node.get( "login_option_" + option_name, default_value );
    if ( initial_selection != null ) box.setSelectedItem( initial_selection );

    JLabel label_label = new JLabel( label );

    options.add( new Option( option_name, require_entry, box, label_label ) );

    label_label.setFont( user_label.getFont() );

    if ( require_entry ) {
      ChangeLabelAction.createStandardErrorLabel( validator, label_label,
        new HasSelectionValidityCheck( box ) );
      vcr.add( box, box );
    }

    component_builder.append( label_label, box );
    pack();
  }


  /**
   * Add a string option to the information provided in the login dialog.
   *
   * @param option_name    The name of the option, this will be given as the key
   *               in the options map.
   * @param label        The String that will be shown on the label.
   * @param default_value    Initial value, if any.
   * @param require_entry    Determines whether or not something must be entered in the
   *               field in order to attempt login.
   */
  public void addOption( String option_name, String label, String default_value,
    boolean require_entry ) {

    JTextField field = new JTextField( default_value );
    field.setName( option_name );

    String initial_value =
      pref_node.get( "login_option_" + option_name, default_value );
    if ( initial_value != null ) field.setText( initial_value );

    JLabel label_label = new JLabel( label );

    options.add( new Option( option_name, require_entry, field, label_label ) );

    if ( require_entry ) {
      ChangeLabelAction.createStandardErrorLabel( validator, label_label,
        new HasTextValidityCheck( field ) );
      vcr.add( field, field );
      validator.check();
    }

    label_label.setFont( user_label.getFont() );
    component_builder.append( label_label, field );
    pack();
  }


  /**
   * Add a boolean option to the information provided in the login dialog.
   */
  public void addOption( String option_name, String label, boolean default_value ) {
    JCheckBox check_box = new JCheckBox( ( String ) null );
    check_box.setName( option_name );

    boolean initial_value = pref_node.getBoolean( "login_option_" + option_name,
      default_value );
    check_box.setSelected( initial_value );

    JLabel label_label = new JLabel( label );

    options.add( new Option( option_name, check_box, label_label ) );

    label_label.setFont( user_label.getFont() );

    component_builder.append( label_label, check_box );
    pack();
  }


  public void addOption( String label, JComponent component ) {
    JLabel label_label = new JLabel( label );
    options.add( new Option( label, component, label_label ) );

    component_builder.append( label_label, component );
    pack();
  }


  /**
   * Add a component directly. The component will span both columns.
   *
   * @param disabled_normally                 If true, the component will be disabled
   *                                          when not attempting to log in.
   * @param disabled_during_login_attempt     If true, the component will be disabled
   *                                          while attempting to log in.
   */
  public void addComponentDirect( JComponent component, boolean disabled_normally,
    boolean disabled_during_login_attempt ) {

    if ( disabled_normally ) components_disabled_normally.add( component );
    if ( disabled_during_login_attempt ) {
      components_disabled_during_login.add( component );
    }

    if ( disabled_normally ) component.setEnabled( false );

    component_builder.append( component, 3 );
    pack();
  }


  public boolean wasCanceled() {
    return canceled;
  }


  public boolean hadError() {
    return error != null;
  }

  public LoginException getError() {
    return error;
  }


  public T getToken() {
    return token;
  }


  public String getUsername() {
    return username;
  }


  public boolean isDevMode() {
    return dev_mode_check.isSelected();
  }


  public Object getOptionValue( String option_name ) {
    for( Option option : options ) {
      if ( option.name.equals( option_name ) ) {
        return option.getValue();
      }
    }

//    assert false : "Option not found: " + option_name;
    return null;
  }


  public void setCloseHandler( Runnable handler ) {
    close_handler = handler;
  }


  public void setCustomWaitSpinner( WaitSpinner2 spinner ) {
    card_panel.remove( this.spinner );

    spinner.stop(); // make sure it's stopped
    this.spinner = spinner;


    card_panel.add( spinner, SPINNER_KEY );
  }


  private void attemptLogin() {
    assert SwingUtilities.isEventDispatchThread();

    toggleFields( false );

    card_layout.show( card_panel, SPINNER_KEY );
    spinner.start();

    username = user_field.getText();

    login_thread = new LoginAttempt();
    login_thread.start();
  }


  private void init( JComponent header_component, String version_string ) {
    setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );

    user_field = new JTextField( username, FIELD_COLUMNS );
    user_field.setName( "user" );
    user_label = new JLabel( "User:" );
    user_label.setLabelFor( user_field );
    user_label.setDisplayedMnemonic( 'U' );
    user_field.addActionListener( new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        if ( password_field.getPassword() == null ||
          password_field.getPassword().length == 0 ) {

          assert SwingUtilities.isEventDispatchThread();

          password_field.requestFocus();
          user_field.setSelectionStart( 0 );
          user_field.setSelectionEnd( 0 );
        }
        else {
          ok_button.doClick();
        }
      }
    } );
    new SelectOnFocusListener( user_field );

    password_field = new JPasswordField( FIELD_COLUMNS );
    password_field.setName( "password" );
    password_label = new JLabel( "Password:" );
    password_label.setLabelFor( password_field );
    password_label.setDisplayedMnemonic( 'P' );
    password_field.addActionListener( new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        ok_button.doClick();
      }
    } );
    new SelectOnFocusListener( password_field );

    ok_button = new JButton( "OK" );
    ok_button.setEnabled( false );
    ok_button.setMnemonic( 'O' );
    ok_button.addActionListener( new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        attemptLogin();
      }
    } );

    cancel_button = new JButton( cancel_or_quit ? "Cancel" : "Quit" );
    cancel_button.setMnemonic( cancel_or_quit ? 'C' : 'Q' );
    cancel_button.addActionListener( new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        if ( login_thread != null ) login_thread.interrupt();

        canceled = true;
        setVisible( false );
        dispose();
      }
    } );

    dev_mode_label = new JLabel( "Dev Mode:" );
    dev_mode_check = new JCheckBox();
    dev_mode_check.setSelected( pref_node.getBoolean( "dev_mode", false ) );

    JPanel content_pane = new JPanel( new BorderLayout() );
    setContentPane( content_pane );

    if ( header_component != null ) {
      content_pane.add( header_component, BorderLayout.NORTH );
    }
    else content_pane.setBorder( new EmptyBorder( 5, 0, 0, 0 ) );

    component_builder.getLayout().setHonorsVisibility( false );
    component_builder.append( user_label, user_field );
    component_builder.append( password_label, password_field );
    if ( System.getProperty( "allow_dev_mode" ) != null ) {
      component_builder.append( dev_mode_label, dev_mode_check );
    }
    else dev_mode_check.setSelected( false );// Make sure it's disabled if not shown

    JPanel component_panel = component_builder.getPanel();
    component_panel.setBorder( new EmptyBorder( 24, 9, 24, 9 ) );

    JPanel spacer =
      new JPanel( new FormLayout( "0px:grow, fill:pref, 0px:grow", "fill:pref" ) );
    CellConstraints cc = new CellConstraints();
    spacer.add( component_panel, cc.xy( 2, 1 ) );

    content_pane.add( spacer, BorderLayout.CENTER );

    card_panel.add( label, LABEL_KEY );
    card_panel.add( spinner, SPINNER_KEY );

    ButtonBarBuilder builder = new ButtonBarBuilder();
    builder.addGrowing( card_panel );
    builder.addGlue();
    builder.addButton( ok_button, cancel_button );

    JPanel button_panel = builder.getPanel();
    button_panel.setBorder( new EmptyBorder( 0, 12, 12, 12 ) );

    content_pane.add( builder.getContainer(), BorderLayout.SOUTH );
    getRootPane().setDefaultButton( ok_button );

    addWindowListener( new WindowAdapter() {
      public void windowOpened( WindowEvent e ) {
        if ( user_field.getText() == null || user_field.getText().length() == 0 ) {
          user_field.requestFocus();
        }
        else if ( password_field.getPassword() == null ||
          password_field.getPassword().length == 0 ) {

          assert SwingUtilities.isEventDispatchThread();

          password_field.requestFocus();
          user_field.setSelectionStart( 0 );
          user_field.setSelectionEnd( 0 );
        }
        else {
          ok_button.requestFocus();
        }
      }

      public void windowClosing( WindowEvent e ) {
        cancel_button.doClick();
      }

      @Override
      public void windowClosed( WindowEvent e ) {
        if ( close_handler != null ) close_handler.run();
      }
    } );
   
    setResizable( false );

    setLabel( version_string );

    pack();

    int width = Math.max( MIN_WIDTH, getWidth() );
    if ( width > MAX_WIDTH ) {
      System.err.println( "LoginDialog max width hit (" + width +
        "). Width will be capped." );
      width = MAX_WIDTH;
    }
    int height = Math.max( MIN_HEIGHT, getHeight() );
    setSize( new Dimension( width, height ) );

    KeyListener cancel_listener = new KeyAdapter() {
      public void keyPressed( KeyEvent e ) {
        if ( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
          cancel_button.doClick();
        }
      }
    };
    addKeyListener( cancel_listener );
    user_field.addKeyListener( cancel_listener );
    password_field.addKeyListener( cancel_listener );

    WindowKit.centerWindow( this );

    validator = new FieldValidityChecker(
      new EnableComponentAction( ok_button ),
      new DisableComponentAction( ok_button ), false );
    //noinspection unchecked
    vcr = new ValueChangeRelay<JComponent>(
      ( ValueChangeRelay.ValueChangeListener<JComponent> )
      validator.createVCRListener() );
    ChangeLabelAction.createStandardErrorLabel( validator, user_label,
      new HasTextValidityCheck( user_field ) );
    vcr.add( user_field, user_field );
    ChangeLabelAction.createStandardErrorLabel( validator, password_label,
      new HasTextValidityCheck( password_field ) );
    vcr.add( password_field, password_field );
    validator.check();
  }


  private void toggleFields( final boolean enable ) {
    // Make sure we do this in the EDT
    if ( !SwingUtilities.isEventDispatchThread() ) {
      SwingUtilities.invokeLater( new Runnable() {
        public void run() {
          toggleFields( enable );
        }
      } );
      return;
    }

    user_label.setEnabled( enable );
    user_field.setEnabled( enable );
    password_label.setEnabled( enable );
    password_field.setEnabled( enable );
    ok_button.setEnabled( enable );
    for ( Option option : options ) {
      option.component_label.setEnabled( enable );
      option.component.setEnabled( enable );
    }

    dev_mode_label.setEnabled( enable );
    dev_mode_check.setEnabled( enable );

    if ( enable ) {
      for( JComponent component : components_disabled_during_login ) {
        component.setEnabled( true );
      }
      for( JComponent component : components_disabled_normally ) {
        component.setEnabled( false );
      }
    }
    else {
      for( JComponent component : components_disabled_normally ) {
        component.setEnabled( true );
      }
      for( JComponent component : components_disabled_during_login ) {
        component.setEnabled( false );
      }
    }

    if ( enable ) {
      assert SwingUtilities.isEventDispatchThread();

      password_field.requestFocus();
      user_field.setSelectionStart( 0 );
      user_field.setSelectionEnd( 0 );
    }

    if ( enable ) validator.check();
  }


  private void setError( final LoginException error ) {
    this.error = error;

    SwingUtilities.invokeLater( new Runnable() {
      public void run() {
        String message;
        if ( error.getMessage() == null ) {
          message = "An unexpected error occurred while logging in:\n" + error;
        }
        else message = error.getMessage();

        toggleFields( true );
        password_field.setText( null );

        ExceptionHandler.handle( LoginFrame.this, error, message );
      }
    } );
  }


  private void setLabel( final String message ) {
    SwingUtilities.invokeLater( new Runnable() {
      public void run() {
        label.setText( message );
        label.setFont( UIKit.modifyFontSize( new JLabel().getFont(), -2 ) );

        card_layout.show( card_panel, LABEL_KEY );
      }
    } );
  }

  private class LoginAttempt extends Thread {
    public void run() {
      boolean reenable_components = true;

      try {
        Map<String,Object> option_map = new HashMap<String,Object>();
        if ( !options.isEmpty() ) {
          for ( Option option : options ) {
            option_map.put( option.name, option.getValue() );
          }
        }


        T token = authenticator.authenticate( user_field.getText(),
          password_field.getPassword(), option_map,
          dev_mode_check.isSelected() );

        if ( isInterrupted() ) return;

        // It worked! Save the token and clear errors
        LoginFrame.this.token = token;
        LoginFrame.this.error = null;

        final JFrame me = LoginFrame.this;
        reenable_components = false;
        SwingUtilities.invokeLater( new Runnable() {
          public void run() {
            me.setVisible( false );

            // Save the options
            for( Option option : options ) {
              String option_pref_name = "login_option_" + option.name;

              if ( option.getValue() == null ) {
                pref_node.remove( option_pref_name );
                continue;
              }

              if ( option.component instanceof JComboBox ) {
                pref_node.put( option_pref_name,
                  option.getValue().toString() );
              }
              else if ( option.component instanceof JCheckBox ) {
                pref_node.putBoolean( option_pref_name,
                  ( ( Boolean ) option.getValue() ).booleanValue() );
              }
              else if ( option.component instanceof JTextComponent ) {
                pref_node.put( option_pref_name,
                  ( ( String ) option.getValue() ) );
              }
            }
            pref_node.putBoolean( "dev_mode", dev_mode_check.isSelected() );

            me.dispose();

          }
        } );
      }
      catch( LoginException ex ) {
//        ex.printStackTrace();
        setError( ex );
      }
      catch( Exception ex ) {
//        ex.printStackTrace();
        String message;
        if ( ex.getMessage() == null ) message = ex.toString();
        else message = ex.getMessage();
        setError( new LoginException( message, ex ) );
      }
      finally {
        if ( reenable_components ) toggleFields( true );
        spinner.stop();
      }
    }
  }


  private class Option {
    private String name;
    private boolean require_entry;

    private JComponent component;
    private JLabel component_label;

    Option( String name, boolean require_entry, JComponent component,
      JLabel component_label ) {

      this.name = name;
      this.require_entry = require_entry;
      this.component = component;
      this.component_label = component_label;
    }

    Option( String name, JComponent component, JLabel component_label ) {
      this( name, true, component, component_label );
    }


    Object getValue() {
      if ( component instanceof JComboBox ) {
        return ( ( JComboBox ) component ).getSelectedItem();
      }
      else if ( component instanceof JCheckBox ) {
        return Boolean.valueOf( ( ( JCheckBox ) component ).isSelected() );
      }
      else if ( component instanceof JTextComponent ) {
        return ( ( JTextComponent ) component ).getText();
      }
      else {
        throw new UnsupportedOperationException(
          "Only combo and check boxes are currently supported." );
      }
    }


    boolean isValueOK() {
      if ( !require_entry ) return true;

      if ( component instanceof JComboBox ) {
        return ( ( JComboBox ) component ).getSelectedItem() != null;
      }
      else if ( component instanceof JTextComponent ) {
        return !StringKit.isEmpty( ( ( JTextComponent ) component ).getText()  );
      }

      return true;
    }
  }
}
TOP

Related Classes of com.starlight.ui.login.LoginFrame$LoginAttempt

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.