components/visual-panels/core/src/java/vpanels/client/com/oracle/solaris/vp/client/swing/LoginPane.java
changeset 827 0944d8c0158b
equal deleted inserted replaced
826:c6aad84d2493 827:0944d8c0158b
       
     1 /*
       
     2  * CDDL HEADER START
       
     3  *
       
     4  * The contents of this file are subject to the terms of the
       
     5  * Common Development and Distribution License (the "License").
       
     6  * You may not use this file except in compliance with the License.
       
     7  *
       
     8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
     9  * or http://www.opensolaris.org/os/licensing.
       
    10  * See the License for the specific language governing permissions
       
    11  * and limitations under the License.
       
    12  *
       
    13  * When distributing Covered Code, include this CDDL HEADER in each
       
    14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    15  * If applicable, add the following below this CDDL HEADER, with the
       
    16  * fields enclosed by brackets "[]" replaced with your own identifying
       
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
       
    18  *
       
    19  * CDDL HEADER END
       
    20  */
       
    21 
       
    22 /*
       
    23  * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
       
    24  */
       
    25 
       
    26 package com.oracle.solaris.vp.client.swing;
       
    27 
       
    28 import java.awt.*;
       
    29 import java.util.*;
       
    30 import java.util.List;
       
    31 import javax.swing.*;
       
    32 import javax.swing.event.*;
       
    33 import javax.swing.text.JTextComponent;
       
    34 import com.oracle.solaris.vp.panel.common.*;
       
    35 import com.oracle.solaris.vp.panel.common.action.*;
       
    36 import com.oracle.solaris.vp.util.misc.DialogMessage;
       
    37 import com.oracle.solaris.vp.util.misc.finder.Finder;
       
    38 import com.oracle.solaris.vp.util.swing.*;
       
    39 import com.oracle.solaris.vp.util.swing.layout.*;
       
    40 
       
    41 @SuppressWarnings({"serial"})
       
    42 public class LoginPane extends DialogPane {
       
    43     //
       
    44     // Inner classes
       
    45     //
       
    46 
       
    47     private static interface LoginField<T> {
       
    48 	T getValue();
       
    49 
       
    50 	void requestEditFocus();
       
    51 
       
    52 	void setEditable(boolean editable);
       
    53 
       
    54 	void setValue(T text);
       
    55 
       
    56 	void setVisible(boolean visible);
       
    57     }
       
    58 
       
    59     private static abstract class DualLoginField<T, C extends Component>
       
    60 	extends JPanel implements LoginField<T> {
       
    61 
       
    62 	//
       
    63 	// Instance data
       
    64 	//
       
    65 
       
    66 	private JLabel label;
       
    67 	private C field;
       
    68 
       
    69 	//
       
    70 	// Constructors
       
    71 	//
       
    72 
       
    73 	public DualLoginField() {
       
    74 	    super(new BorderLayout());
       
    75 	    setOpaque(false);
       
    76 
       
    77 	    label = new JLabel();
       
    78 	    add(label, BorderLayout.WEST);
       
    79 
       
    80 	    field = createField();
       
    81 	    add(field, BorderLayout.NORTH);
       
    82 	}
       
    83 
       
    84 	//
       
    85 	// LoginField methods
       
    86 	//
       
    87 
       
    88 	@Override
       
    89 	public void requestEditFocus() {
       
    90 	    field.requestFocusInWindow();
       
    91 	}
       
    92 
       
    93 	@Override
       
    94 	public void setEditable(boolean editable) {
       
    95 	    label.setVisible(!editable);
       
    96 	    field.setVisible(editable);
       
    97 	}
       
    98 
       
    99 	//
       
   100 	// DualLoginField methods
       
   101 	//
       
   102 
       
   103 	public abstract C createField();
       
   104 
       
   105 	public JLabel getLabel() {
       
   106 	    return label;
       
   107 	}
       
   108 
       
   109 	public C getField() {
       
   110 	    return field;
       
   111 	}
       
   112 
       
   113 	public String toDisplayableText(T value) {
       
   114 	    return value == null ? null : value.toString();
       
   115 	}
       
   116 
       
   117 	protected void updateLabel() {
       
   118 	    getLabel().setText(toDisplayableText(getValue()));
       
   119 	}
       
   120     }
       
   121 
       
   122     private static class LoginTextField
       
   123 	extends DualLoginField<String, JTextField> {
       
   124 
       
   125 	//
       
   126 	// LoginField methods
       
   127 	//
       
   128 
       
   129 	@Override
       
   130 	public String getValue() {
       
   131 	    String value = getField().getText();
       
   132 	    if (value.isEmpty()) {
       
   133 		value = null;
       
   134 	    }
       
   135 	    return value;
       
   136 	}
       
   137 
       
   138 	@Override
       
   139 	public void requestEditFocus() {
       
   140 	    getField().selectAll();
       
   141 	    super.requestEditFocus();
       
   142 	}
       
   143 
       
   144 	@Override
       
   145 	public void setValue(String text) {
       
   146 	    getField().setText(text);
       
   147 	}
       
   148 
       
   149 	//
       
   150 	// DualLoginField methods
       
   151 	//
       
   152 
       
   153 	@Override
       
   154 	public JTextField createField() {
       
   155 	    JTextField field = new JTextField(GUIUtil.getTextFieldWidth());
       
   156 
       
   157 	    field.getDocument().addDocumentListener(
       
   158 		new DocumentAdapter() {
       
   159 		    @Override
       
   160 		    public void docUpdate(DocumentEvent e) {
       
   161 			updateLabel();
       
   162 		    }
       
   163 		});
       
   164 
       
   165 	    return field;
       
   166 	}
       
   167     }
       
   168 
       
   169     private static class LoginComboBox
       
   170 	extends DualLoginField<String, JComboBox> {
       
   171 
       
   172 	//
       
   173 	// Instance data
       
   174 	//
       
   175 
       
   176 	private String nullText;
       
   177 
       
   178 	//
       
   179 	// Constructors
       
   180 	//
       
   181 
       
   182 	public LoginComboBox(String nullText) {
       
   183 	    this.nullText = nullText;
       
   184 	    updateLabel();
       
   185 	}
       
   186 
       
   187 	//
       
   188 	// LoginField methods
       
   189 	//
       
   190 
       
   191 	@Override
       
   192 	public String getValue() {
       
   193 	    return (String)getField().getSelectedItem();
       
   194 	}
       
   195 
       
   196 	@Override
       
   197 	public void setValue(String text) {
       
   198 	    getField().getModel().setSelectedItem(text);
       
   199 	}
       
   200 
       
   201 	//
       
   202 	// DualLoginField methods
       
   203 	//
       
   204 
       
   205 	@Override
       
   206 	public JComboBox createField() {
       
   207 	    JComboBox field = new JComboBox();
       
   208 	    if (!(field.getModel() instanceof DefaultComboBoxModel)) {
       
   209 		field.setModel(new DefaultComboBoxModel());
       
   210 	    }
       
   211 
       
   212 	    final ListCellRenderer renderer = field.getRenderer();
       
   213 
       
   214 	    // Handle null values in the model
       
   215 	    field.setRenderer(
       
   216 		new ListCellRenderer() {
       
   217 		    @Override
       
   218                     public Component getListCellRendererComponent(JList list,
       
   219 			Object value, int index, boolean isSelected,
       
   220 			boolean cellHasFocus) {
       
   221 
       
   222                         return renderer.getListCellRendererComponent(list,
       
   223 			    toDisplayableText((String)value), index, isSelected,
       
   224 			    cellHasFocus);
       
   225 		    }
       
   226                 });
       
   227 
       
   228 	    field.getModel().addListDataListener(
       
   229 		new ListDataListener() {
       
   230 		    @Override
       
   231 		    public void contentsChanged(ListDataEvent e) {
       
   232 			updateLabel();
       
   233 		    }
       
   234 
       
   235 		    @Override
       
   236 		    public void intervalAdded(ListDataEvent e) {
       
   237 			updateLabel();
       
   238 		    }
       
   239 
       
   240 		    @Override
       
   241 		    public void intervalRemoved(ListDataEvent e) {
       
   242 			updateLabel();
       
   243 		    }
       
   244 		});
       
   245 
       
   246 	    return field;
       
   247 	}
       
   248 
       
   249 	@Override
       
   250 	public String toDisplayableText(String value) {
       
   251 	    return value == null ? nullText : value;
       
   252 	}
       
   253     }
       
   254 
       
   255     private static class LoginCheckBox extends JCheckBox
       
   256 	implements LoginField<Boolean> {
       
   257 
       
   258 	//
       
   259 	// Instance data
       
   260 	//
       
   261 
       
   262 	private UneditableToggleButtonModel model;
       
   263 
       
   264 	//
       
   265 	// Constructors
       
   266 	//
       
   267 
       
   268 	public LoginCheckBox() {
       
   269 	    model = new UneditableToggleButtonModel();
       
   270 	    setModel(model);
       
   271 	}
       
   272 
       
   273 	//
       
   274 	// LoginField methods
       
   275 	//
       
   276 
       
   277 	@Override
       
   278 	public Boolean getValue() {
       
   279 	    return isSelected();
       
   280 	}
       
   281 
       
   282 	@Override
       
   283 	public void requestEditFocus() {
       
   284 	    requestFocusInWindow();
       
   285 	}
       
   286 
       
   287 	@Override
       
   288 	public void setEditable(boolean editable) {
       
   289 	    model.setEditable(editable);
       
   290 	}
       
   291 
       
   292 	@Override
       
   293 	public void setValue(Boolean checked) {
       
   294 	    model.setSelectedForced(checked != null && checked);
       
   295 	}
       
   296     }
       
   297 
       
   298     //
       
   299     // Static data
       
   300     //
       
   301 
       
   302     private static final Color COLOR_LABEL_ERROR = Color.red.darker();
       
   303 
       
   304     private static final String LABEL_HOST =
       
   305 	Finder.getString("login.label.host");
       
   306 
       
   307     private static final String LABEL_USER =
       
   308 	Finder.getString("login.label.user");
       
   309 
       
   310     private static final String LABEL_ROLE =
       
   311 	Finder.getString("login.label.role");
       
   312 
       
   313     private static final String LABEL_ZONEPROMPT =
       
   314 	Finder.getString("login.label.zoneprompt");
       
   315 
       
   316     private static final String LABEL_ZONE =
       
   317 	Finder.getString("login.label.zone");
       
   318 
       
   319     private static final String LABEL_ZONEUSER =
       
   320 	Finder.getString("login.label.zoneuser");
       
   321 
       
   322     private static final String LABEL_ZONEROLE =
       
   323 	Finder.getString("login.label.zonerole");
       
   324 
       
   325     //
       
   326     // Instance data
       
   327     //
       
   328 
       
   329     private JPanel fieldPanel;
       
   330     private int nCoreFields;
       
   331 
       
   332     private JLabel hostLabel;
       
   333     private LoginTextField hostField;
       
   334 
       
   335     private JLabel userLabel;
       
   336     private LoginTextField userField;
       
   337 
       
   338     private JLabel roleLabel;
       
   339     private LoginComboBox roleField;
       
   340 
       
   341     private LoginCheckBox zonePromptField;
       
   342 
       
   343     private JLabel zoneLabel;
       
   344     private LoginComboBox zoneField;
       
   345 
       
   346     private JLabel zoneUserLabel;
       
   347     private LoginTextField zoneUserField;
       
   348 
       
   349     private JLabel zoneRoleLabel;
       
   350     private LoginComboBox zoneRoleField;
       
   351 
       
   352     //
       
   353     // Constructors
       
   354     //
       
   355 
       
   356     public LoginPane() {
       
   357 	createFieldPanel();
       
   358 
       
   359 	zonePromptField = new LoginCheckBox();
       
   360 
       
   361 	int gap = GUIUtil.getHalfGap();
       
   362 	JPanel panel = new JPanel(new BorderLayout(gap, gap));
       
   363 	panel.setOpaque(false);
       
   364 	panel.add(fieldPanel, BorderLayout.CENTER);
       
   365 	panel.add(zonePromptField, BorderLayout.SOUTH);
       
   366 
       
   367 	setContent(panel, false, false);
       
   368 
       
   369 	SettingsButtonBar buttonBar = getButtonBar();
       
   370 
       
   371 	JButton cancel = buttonBar.getCancelButton();
       
   372 	cancel.addActionListener(setClickedButtonActionListener);
       
   373 
       
   374 	// Move to left side of dialog
       
   375 	buttonBar.remove(cancel);
       
   376 	buttonBar.add(cancel, 0);
       
   377 
       
   378 	JButton close = buttonBar.getCloseButton();
       
   379 	close.addActionListener(setClickedButtonActionListener);
       
   380 
       
   381 	JButton back = buttonBar.getBackButton();
       
   382 	back.addActionListener(setClickedButtonActionListener);
       
   383 
       
   384 	JButton forward = buttonBar.getForwardButton();
       
   385 	forward.addActionListener(setClickedButtonActionListener);
       
   386 	forward.setText(Finder.getString("login.button.forward"));
       
   387     }
       
   388 
       
   389     //
       
   390     // LoginPane methods
       
   391     //
       
   392 
       
   393     public void promptForAck(LoginRequest request)
       
   394 	throws ActionAbortedException {
       
   395 
       
   396 	assert !EventQueue.isDispatchThread();
       
   397 
       
   398 	final LoginProperty<String> host = request.getHost();
       
   399 	final LoginProperty<String> user = request.getUser();
       
   400 	final LoginProperty<String> role = request.getRole();
       
   401 	final LoginProperty<String> zone = request.getZone();
       
   402 	final LoginProperty<String> zoneUser = request.getZoneUser();
       
   403 	final LoginProperty<String> zoneRole = request.getZoneRole();
       
   404 	final List<DialogMessage> messages = request.getMessages();
       
   405 
       
   406 	addMessage(request, "login.message.ack");
       
   407 
       
   408 	GUIUtil.invokeAndWait(
       
   409 	    new Runnable() {
       
   410 		@Override
       
   411 		public void run() {
       
   412 		    getMessagePanel().setMessages(messages);
       
   413 
       
   414 		    clear();
       
   415 		    setHostInUI(host, false);
       
   416 		    setUserInUI(user, false);
       
   417 
       
   418 		    if (role.getValue() != null) {
       
   419 			setRoleInUI(role, false);
       
   420 		    }
       
   421 
       
   422 		    if (zone.getValue() != null) {
       
   423 			setZoneInUI(zone, false);
       
   424 			if (zoneUser.getValue() != null) {
       
   425 			    setZoneUserInUI(zoneUser, false);
       
   426 			    if (zoneRole.getValue() != null) {
       
   427 				setZoneRoleInUI(zoneRole, false);
       
   428 			    }
       
   429 			}
       
   430 		    }
       
   431 
       
   432 		    getButtonBar().getBackButton().setEnabled(false);
       
   433 		}
       
   434 	});
       
   435 
       
   436 	try {
       
   437 	    awaitForward();
       
   438 	} catch (ActionRegressedException impossible) {
       
   439 	}
       
   440     }
       
   441 
       
   442     public void promptForAuth(LoginRequest request,
       
   443         final List<LoginProperty> properties, final boolean isZone,
       
   444 	final boolean isUserAuth, boolean isFirst)
       
   445 	throws ActionAbortedException, ActionRegressedException {
       
   446 
       
   447 	assert !EventQueue.isDispatchThread();
       
   448 
       
   449 	final LoginProperty<String> host = request.getHost();
       
   450 	final LoginProperty<String> user = request.getUser();
       
   451 	final LoginProperty<Boolean> zonePrompt = request.getZonePrompt();
       
   452 	final LoginProperty<String> role = request.getRole();
       
   453 	final LoginProperty<String> zone = request.getZone();
       
   454 	final LoginProperty<String> zoneUser = request.getZoneUser();
       
   455 	final LoginProperty<String> zoneRole = request.getZoneRole();
       
   456 	final List<DialogMessage> messages = request.getMessages();
       
   457 
       
   458 	if (isFirst) {
       
   459 	    String message = "login.message.auth.";
       
   460 	    if (isZone) {
       
   461 		message += "zone.";
       
   462 	    }
       
   463 	    message += isUserAuth ? "user" : "role";
       
   464 	    addMessage(request, message);
       
   465 	}
       
   466 
       
   467 	final Map<LoginProperty, JTextComponent> map =
       
   468 	    new HashMap<LoginProperty, JTextComponent>();
       
   469 
       
   470 	GUIUtil.invokeAndWait(
       
   471 	    new Runnable() {
       
   472 		@Override
       
   473 		public void run() {
       
   474 		    getMessagePanel().setMessages(messages);
       
   475 
       
   476 		    clear();
       
   477 		    setHostInUI(host, false);
       
   478 
       
   479 		    if (isZone) {
       
   480 			setZoneInUI(zone, false);
       
   481 			setZoneUserInUI(zoneUser, false);
       
   482 			if (!isUserAuth) {
       
   483 			    setZoneRoleInUI(zoneRole, false);
       
   484 			}
       
   485 		    } else {
       
   486 			setUserInUI(user, false);
       
   487 			if (!isUserAuth) {
       
   488 			    setRoleInUI(role, false);
       
   489 			}
       
   490 		    }
       
   491 
       
   492 		    boolean editable = host.isEditable() || user.isEditable() ||
       
   493                         zonePrompt.isEditable();
       
   494 		    if (isZone) {
       
   495                         editable |= role.isEditable() || zone.isEditable() ||
       
   496                             zoneUser.isEditable() || (!isUserAuth &&
       
   497                             zoneRole.isEditable());
       
   498 		    } else {
       
   499                         editable |= !isUserAuth && role.isEditable();
       
   500 		    }
       
   501 
       
   502                     getButtonBar().getBackButton().setEnabled(editable);
       
   503 
       
   504 		    int cols = GUIUtil.getTextFieldWidth();
       
   505 
       
   506 		    if (properties != null) {
       
   507 			for (LoginProperty property : properties) {
       
   508 			    JLabel label = new JLabel(property.getName());
       
   509 			    JTextComponent field = null;
       
   510 			    Object value = property.getValue();
       
   511 			    String text;
       
   512 			    if (property instanceof PasswordLoginProperty) {
       
   513 				field = new JPasswordField(cols);
       
   514 				text = new String((char[])value);
       
   515 			    } else {
       
   516 				field = new JTextField(cols);
       
   517 				text = (String)value;
       
   518 			    }
       
   519 
       
   520 			    field.setText(text);
       
   521 			    fieldPanel.add(label);
       
   522 			    fieldPanel.add(field);
       
   523 			    map.put(property, field);
       
   524 			}
       
   525 		    }
       
   526 		}
       
   527 	    });
       
   528 
       
   529 	final boolean[] success = {false};
       
   530 	try {
       
   531 	    awaitForward();
       
   532 	    success[0] = true;
       
   533 	} finally {
       
   534 	    GUIUtil.invokeAndWait(
       
   535 		new Runnable() {
       
   536 		    @Override
       
   537 		    public void run() {
       
   538 			for (Map.Entry<LoginProperty, JTextComponent> entry :
       
   539 			    map.entrySet()) {
       
   540 
       
   541 			    JTextComponent field = entry.getValue();
       
   542 
       
   543 			    if (success[0]) {
       
   544 				if (field instanceof JPasswordField) {
       
   545 				    @SuppressWarnings({"unchecked"})
       
   546 				    LoginProperty<char[]> prop =
       
   547 					(LoginProperty<char[]>)entry.getKey();
       
   548 				    prop.setValue(
       
   549 					((JPasswordField)field).getPassword());
       
   550 				} else {
       
   551 				    @SuppressWarnings({"unchecked"})
       
   552 				    LoginProperty<String> prop =
       
   553 					(LoginProperty<String>)entry.getKey();
       
   554 				    prop.setValue(field.getText());
       
   555 				}
       
   556 			    }
       
   557 
       
   558 			    // Remove any sensitive information
       
   559 			    field.setText(null);
       
   560 			}
       
   561 		    }
       
   562 		});
       
   563 	}
       
   564     }
       
   565 
       
   566     public void promptForFailedRequest(LoginRequest request) {
       
   567 	assert !EventQueue.isDispatchThread();
       
   568 
       
   569 	final LoginProperty<String> host = request.getHost();
       
   570 	final LoginProperty<String> user = request.getUser();
       
   571 	final LoginProperty<String> role = request.getRole();
       
   572 	final LoginProperty<String> zone = request.getZone();
       
   573 	final LoginProperty<String> zoneUser = request.getZoneUser();
       
   574 	final LoginProperty<String> zoneRole = request.getZoneRole();
       
   575 	final List<DialogMessage> messages = request.getMessages();
       
   576 
       
   577 	if (messages.isEmpty()) {
       
   578 	    addMessage(request, "login.message.fail");
       
   579 	}
       
   580 
       
   581 	GUIUtil.invokeAndWait(
       
   582 	    new Runnable() {
       
   583 		@Override
       
   584 		public void run() {
       
   585 		    getMessagePanel().setMessages(messages);
       
   586 
       
   587 		    clear();
       
   588 		    if (host.getValue() != null) {
       
   589 			setHostInUI(host, false);
       
   590 			if (user.getValue() != null) {
       
   591 			    setUserInUI(user, false);
       
   592 			    if (role.getValue() != null) {
       
   593 				setRoleInUI(role, false);
       
   594 			    }
       
   595 			    if (zone.getValue() != null) {
       
   596 				setZoneInUI(zone, false);
       
   597 				if (zoneUser.getValue() != null) {
       
   598 				    setZoneUserInUI(zoneUser, false);
       
   599 				    if (zoneRole.getValue() != null) {
       
   600 					setZoneRoleInUI(zoneRole, false);
       
   601 				    }
       
   602 				}
       
   603 			    }
       
   604 			}
       
   605 		    }
       
   606 		}
       
   607 	});
       
   608 
       
   609 	awaitClose();
       
   610     }
       
   611 
       
   612     public void promptForHostAndUser(LoginRequest request)
       
   613 	throws ActionAbortedException {
       
   614 
       
   615 	assert !EventQueue.isDispatchThread();
       
   616 
       
   617 	final LoginProperty<String> host = request.getHost();
       
   618 	final LoginProperty<String> user = request.getUser();
       
   619 	final LoginProperty<Boolean> zonePrompt = request.getZonePrompt();
       
   620 	final List<DialogMessage> messages = request.getMessages();
       
   621 
       
   622 	String resource = "login.message.hostuser";
       
   623 	if (host.isEditable()) {
       
   624 	    resource += ".host";
       
   625 	}
       
   626 	if (user.isEditable()) {
       
   627 	    resource += ".user";
       
   628 	}
       
   629 	addMessage(request, resource);
       
   630 
       
   631 	GUIUtil.invokeAndWait(
       
   632 	    new Runnable() {
       
   633 		@Override
       
   634 		public void run() {
       
   635 		    getMessagePanel().setMessages(messages);
       
   636 
       
   637 		    clear();
       
   638 		    setHostInUI(host, true);
       
   639 		    setUserInUI(user, true);
       
   640 
       
   641 		    Boolean zonePromptValue = zonePrompt.getValue();
       
   642                     if ((zonePromptValue != null && zonePromptValue) ||
       
   643 			zonePrompt.isEditable()) {
       
   644 			setZonePromptInUI(zonePrompt, true);
       
   645 		    }
       
   646 
       
   647 		    getButtonBar().getBackButton().setEnabled(false);
       
   648 		}
       
   649 	});
       
   650 
       
   651 	try {
       
   652 	    awaitForward();
       
   653 	} catch (ActionRegressedException impossible) {
       
   654 	}
       
   655 
       
   656 	setInProperty(host, hostField);
       
   657 	setInProperty(user, userField);
       
   658 	setInProperty(zonePrompt, zonePromptField);
       
   659     }
       
   660 
       
   661     public void promptForRole(LoginRequest request,
       
   662         final List<String> roles, final boolean isZone)
       
   663 	throws ActionAbortedException, ActionRegressedException {
       
   664 
       
   665 	assert !EventQueue.isDispatchThread();
       
   666 
       
   667 	final LoginProperty<String> host = request.getHost();
       
   668 	final LoginProperty<String> user = request.getUser();
       
   669 	final LoginProperty<Boolean> zonePrompt = request.getZonePrompt();
       
   670 	final LoginProperty<String> role = request.getRole();
       
   671 	final LoginProperty<String> zone = request.getZone();
       
   672 	final LoginProperty<String> zoneUser = request.getZoneUser();
       
   673 	final LoginProperty<String> zoneRole = request.getZoneRole();
       
   674 	final List<DialogMessage> messages = request.getMessages();
       
   675 
       
   676         addMessage(request, isZone ? "login.message.zonerole" :
       
   677 	    "login.message.role");
       
   678 
       
   679 	GUIUtil.invokeAndWait(
       
   680 	    new Runnable() {
       
   681 		@Override
       
   682 		public void run() {
       
   683 		    getMessagePanel().setMessages(messages);
       
   684 
       
   685 		    LoginComboBox field = isZone ? zoneRoleField : roleField;
       
   686 		    DefaultComboBoxModel model =
       
   687 			(DefaultComboBoxModel)field.getField().getModel();
       
   688 
       
   689 		    model.removeAllElements();
       
   690 		    model.addElement(null);
       
   691 		    for (String r : roles) {
       
   692 			model.addElement(r);
       
   693 		    }
       
   694 
       
   695 		    clear();
       
   696 		    setHostInUI(host, false);
       
   697 
       
   698 		    if (isZone) {
       
   699 			setZoneInUI(zone, false);
       
   700 			setZoneUserInUI(zoneUser, false);
       
   701 			setZoneRoleInUI(zoneRole, true);
       
   702 		    } else {
       
   703 			setUserInUI(user, false);
       
   704 			setRoleInUI(role, true);
       
   705 		    }
       
   706 
       
   707 		    getButtonBar().getBackButton().setEnabled(
       
   708 			host.isEditable() || user.isEditable() ||
       
   709 			zonePrompt.isEditable() ||
       
   710 			(isZone && zone.isEditable() || zoneUser.isEditable()));
       
   711 		}
       
   712 	    });
       
   713 
       
   714 	awaitForward();
       
   715 	if (isZone) {
       
   716 	    setInProperty(zoneRole, zoneRoleField);
       
   717 	} else {
       
   718 	    setInProperty(role, roleField);
       
   719 	}
       
   720     }
       
   721 
       
   722     public void promptForZoneAndUser(LoginRequest request,
       
   723         final List<String> zones) throws ActionAbortedException,
       
   724         ActionRegressedException {
       
   725 
       
   726 	assert !EventQueue.isDispatchThread();
       
   727 
       
   728 	final LoginProperty<String> host = request.getHost();
       
   729 	final LoginProperty<String> user = request.getUser();
       
   730 	final LoginProperty<Boolean> zonePrompt = request.getZonePrompt();
       
   731 	final LoginProperty<String> role = request.getRole();
       
   732 	final LoginProperty<String> zone = request.getZone();
       
   733 	final LoginProperty<String> zoneUser = request.getZoneUser();
       
   734 	final List<DialogMessage> messages = request.getMessages();
       
   735 
       
   736 	String resource = "login.message.zoneuser";
       
   737 	if (zone.isEditable()) {
       
   738 	    resource += ".zone";
       
   739 	}
       
   740 	if (zoneUser.isEditable()) {
       
   741 	    resource += ".user";
       
   742 	}
       
   743 	addMessage(request, resource);
       
   744 
       
   745 	if (zoneUser.getValue() == null) {
       
   746 	    // Prepopulate with sensible default if unset
       
   747 	    zoneUser.setValue(user.getValue());
       
   748 	}
       
   749 
       
   750 	GUIUtil.invokeAndWait(
       
   751 	    new Runnable() {
       
   752 		@Override
       
   753 		public void run() {
       
   754 		    getMessagePanel().setMessages(messages);
       
   755 
       
   756 		    DefaultComboBoxModel model =
       
   757 			(DefaultComboBoxModel)zoneField.getField().getModel();
       
   758 
       
   759 		    model.removeAllElements();
       
   760 		    model.addElement(null);
       
   761 		    for (String z : zones) {
       
   762 			model.addElement(z);
       
   763 		    }
       
   764 
       
   765 		    clear();
       
   766 		    setHostInUI(host, false);
       
   767 		    setZoneInUI(zone, true);
       
   768 		    setZoneUserInUI(zoneUser, true);
       
   769 
       
   770                     getButtonBar().getBackButton().setEnabled(
       
   771                         host.isEditable() || user.isEditable() ||
       
   772                         zonePrompt.isEditable() || role.isEditable());
       
   773 		}
       
   774 	    });
       
   775 
       
   776 	awaitForward();
       
   777 	setInProperty(zone, zoneField);
       
   778 	setInProperty(zoneUser, zoneUserField);
       
   779     }
       
   780 
       
   781     //
       
   782     // Private methods
       
   783     //
       
   784 
       
   785     private void addMessage(LoginRequest request, String resource) {
       
   786 	DialogMessage message = new DialogMessage(Finder.getString(
       
   787             resource, request.getHost().getValue(),
       
   788             request.getUser().getValue(), request.getRole().getValue(),
       
   789             request.getZone().getValue(), request.getZoneUser().getValue(),
       
   790             request.getZoneRole().getValue()));
       
   791 	request.getMessages().add(message);
       
   792     }
       
   793 
       
   794     private void awaitClose() {
       
   795 	SettingsButtonBar bar = getButtonBar();
       
   796 	AbstractButton closeButton = bar.getCloseButton();
       
   797 
       
   798 	for (AbstractButton button : bar.getButtons()) {
       
   799 	    button.setVisible(button == closeButton);
       
   800 	}
       
   801 
       
   802 	while (awaitClickedButton() != closeButton);
       
   803     }
       
   804 
       
   805     private void awaitForward() throws ActionAbortedException,
       
   806 	ActionRegressedException {
       
   807 
       
   808 	SettingsButtonBar bar = getButtonBar();
       
   809 	AbstractButton backButton = bar.getBackButton();
       
   810 	AbstractButton forwardButton = bar.getForwardButton();
       
   811 	AbstractButton cancelButton = bar.getCancelButton();
       
   812 
       
   813 	for (AbstractButton button : bar.getButtons()) {
       
   814 	    button.setVisible(button == backButton ||
       
   815 		button == forwardButton || button == cancelButton);
       
   816 	}
       
   817 
       
   818 	JButton clicked = null;
       
   819 	while (clicked != forwardButton) {
       
   820 	    clicked = awaitClickedButton();
       
   821 
       
   822 	    if (clicked == backButton) {
       
   823 		throw new ActionRegressedException();
       
   824 	    }
       
   825 
       
   826 	    if (clicked == cancelButton) {
       
   827 		throw new ActionAbortedException();
       
   828 	    }
       
   829 	}
       
   830     }
       
   831 
       
   832     private void createFieldPanel() {
       
   833 	hostField = new LoginTextField();
       
   834 	hostLabel = new JLabel();
       
   835 	hostLabel.setLabelFor(hostField);
       
   836 
       
   837 	userField = new LoginTextField();
       
   838 	userLabel = new JLabel();
       
   839 	userLabel.setLabelFor(userField);
       
   840 
       
   841         roleField = new LoginComboBox(
       
   842 	    Finder.getString("login.field.role.none"));
       
   843 	roleLabel = new JLabel();
       
   844 	roleLabel.setLabelFor(roleField);
       
   845 
       
   846         zoneField = new LoginComboBox(
       
   847 	    Finder.getString("login.field.zone.select"));
       
   848 	zoneLabel = new JLabel();
       
   849 	zoneLabel.setLabelFor(zoneField);
       
   850 
       
   851 	zoneUserField = new LoginTextField();
       
   852 	zoneUserLabel = new JLabel();
       
   853 	zoneUserLabel.setLabelFor(zoneUserField);
       
   854 
       
   855         zoneRoleField = new LoginComboBox(
       
   856 	    Finder.getString("login.field.zonerole.none"));
       
   857 	zoneRoleLabel = new JLabel();
       
   858 	zoneRoleLabel.setLabelFor(zoneRoleField);
       
   859 
       
   860 	int gap = GUIUtil.getHalfGap();
       
   861 
       
   862 	HasAnchors a = new SimpleHasAnchors(
       
   863 	    HorizontalAnchor.LEFT, VerticalAnchor.CENTER);
       
   864 
       
   865 	TableLayout layout = new TableLayout(1, 2, gap, gap);
       
   866 	layout.setHorizontalAnchor(HorizontalAnchor.LEFT);
       
   867 	layout.setDefaultConstraint(a);
       
   868 
       
   869 	fieldPanel = new JPanel(layout);
       
   870 	fieldPanel.setOpaque(false);
       
   871 
       
   872 	fieldPanel.add(hostLabel);
       
   873 	fieldPanel.add(hostField);
       
   874 
       
   875 	fieldPanel.add(userLabel);
       
   876 	fieldPanel.add(userField);
       
   877 
       
   878 	fieldPanel.add(roleLabel);
       
   879 	fieldPanel.add(roleField);
       
   880 
       
   881 	fieldPanel.add(zoneLabel);
       
   882 	fieldPanel.add(zoneField);
       
   883 
       
   884 	fieldPanel.add(zoneUserLabel);
       
   885 	fieldPanel.add(zoneUserField);
       
   886 
       
   887 	fieldPanel.add(zoneRoleLabel);
       
   888 	fieldPanel.add(zoneRoleField);
       
   889 
       
   890 	nCoreFields = fieldPanel.getComponentCount();
       
   891     }
       
   892 
       
   893     private void clear() {
       
   894 	assert EventQueue.isDispatchThread();
       
   895 
       
   896 	hostLabel.setVisible(false);
       
   897 	hostField.setVisible(false);
       
   898 
       
   899 	roleLabel.setVisible(false);
       
   900 	roleField.setVisible(false);
       
   901 
       
   902 	userLabel.setVisible(false);
       
   903 	userField.setVisible(false);
       
   904 
       
   905 	zoneLabel.setVisible(false);
       
   906 	zoneField.setVisible(false);
       
   907 
       
   908 	zonePromptField.setVisible(false);
       
   909 
       
   910 	zoneUserLabel.setVisible(false);
       
   911 	zoneUserField.setVisible(false);
       
   912 
       
   913 	zoneRoleLabel.setVisible(false);
       
   914 	zoneRoleField.setVisible(false);
       
   915 
       
   916 	// Remove all but the "core" fields (host, user, role, etc.)
       
   917 	while (fieldPanel.getComponentCount() > nCoreFields) {
       
   918 	    fieldPanel.remove(nCoreFields);
       
   919 	}
       
   920     }
       
   921 
       
   922     private void setHostInUI(LoginProperty<String> property, boolean allowEdit)
       
   923     {
       
   924 	setInUI(property, hostLabel, hostField, LABEL_HOST, allowEdit);
       
   925     }
       
   926 
       
   927     private <T> void setInProperty(LoginProperty<T> property,
       
   928 	LoginField<T> field) {
       
   929 
       
   930 	if (property.isEditable()) {
       
   931 	    property.setValue(field.getValue());
       
   932 	}
       
   933 	property.setErrored(false);
       
   934     }
       
   935 
       
   936     private <T> void setInUI(LoginProperty<T> property, Component label,
       
   937         final LoginField<T> field, String defaultLabel, boolean allowEdit) {
       
   938 
       
   939 	assert EventQueue.isDispatchThread();
       
   940 
       
   941 	String labelText = property.getName();
       
   942 	if (labelText == null) {
       
   943 	    labelText = defaultLabel;
       
   944 	}
       
   945 	if (label instanceof JLabel) {
       
   946 	    ((JLabel)label).setText(labelText);
       
   947 	} else if (label instanceof JCheckBox) {
       
   948 	    ((JCheckBox)label).setText(labelText);
       
   949 	}
       
   950 
       
   951 	boolean editable = allowEdit && property.isEditable();
       
   952 	Color foreground = null;
       
   953 	int style = Font.PLAIN;
       
   954 	if (property.isErrored()) {
       
   955 	    foreground = COLOR_LABEL_ERROR;
       
   956 	    style = Font.BOLD;
       
   957 	    if (editable) {
       
   958 		EventQueue.invokeLater(
       
   959 		    new Runnable() {
       
   960 			@Override
       
   961 			public void run() {
       
   962 			    field.requestEditFocus();
       
   963 			}
       
   964 		    });
       
   965 	    }
       
   966 	}
       
   967 	label.setForeground(foreground);
       
   968 	label.setFont(label.getFont().deriveFont(style));
       
   969 	label.setVisible(true);
       
   970 
       
   971 	field.setValue(property.getValue());
       
   972 	field.setEditable(editable);
       
   973 	field.setVisible(true);
       
   974     }
       
   975 
       
   976     private void setRoleInUI(LoginProperty<String> property, boolean allowEdit)
       
   977     {
       
   978 	setInUI(property, roleLabel, roleField, LABEL_ROLE, allowEdit);
       
   979     }
       
   980 
       
   981     private void setUserInUI(LoginProperty<String> property, boolean allowEdit)
       
   982     {
       
   983 	setInUI(property, userLabel, userField, LABEL_USER, allowEdit);
       
   984     }
       
   985 
       
   986     private void setZoneInUI(LoginProperty<String> property, boolean allowEdit)
       
   987     {
       
   988 	setInUI(property, zoneLabel, zoneField, LABEL_ZONE, allowEdit);
       
   989     }
       
   990 
       
   991     private void setZonePromptInUI(LoginProperty<Boolean> property,
       
   992 	boolean allowEdit) {
       
   993         setInUI(property, zonePromptField, zonePromptField, LABEL_ZONEPROMPT,
       
   994 	    allowEdit);
       
   995     }
       
   996 
       
   997     private void setZoneRoleInUI(LoginProperty<String> property,
       
   998 	boolean allowEdit) {
       
   999 
       
  1000         setInUI(property, zoneRoleLabel, zoneRoleField, LABEL_ZONEROLE,
       
  1001 	    allowEdit);
       
  1002     }
       
  1003 
       
  1004     private void setZoneUserInUI(LoginProperty<String> property,
       
  1005 	boolean allowEdit) {
       
  1006         setInUI(property, zoneUserLabel, zoneUserField, LABEL_ZONEUSER,
       
  1007 	    allowEdit);
       
  1008     }
       
  1009 }