/* StringDialog.java A configurable Dialog box. Created: 16 June 1997 Module By: Michael Mulvaney ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2010 The University of Texas at Austin Contact information Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package arlut.csd.JDialog; import java.awt.BorderLayout; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Date; import java.util.Hashtable; import java.util.Vector; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.SwingConstants; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.EtchedBorder; import arlut.csd.JDataComponent.JcalendarField; import arlut.csd.JDataComponent.JpassField; import arlut.csd.JDataComponent.JpasswordField; import arlut.csd.JDataComponent.JstringArea; import arlut.csd.JDataComponent.JstringField; import arlut.csd.JDataComponent.TimedKeySelectionManager; import arlut.csd.Util.TranslationService; /*------------------------------------------------------------------------------ class StringDialog ------------------------------------------------------------------------------*/ /** * <p>A simple customizable dialog with support for a variety of data * field components.</p> * * <p>For simple dialogs, use the included constructors. For more * complicated dialogs, including check boxes, choice lists, and text * fields, use a {@link arlut.csd.JDialog.DialogRsrc DialogRsrc} object * to pass in a pre-defined dialog definition.</p> * * <p>The ShowDialog method shows the current dialog, and returns a * Hashtable of results, which map the label used in the dialog for * individual data fields with the value entered into that field.</p> * * @see DialogRsrc * @version $Id$ * @author Mike Mulvaney */ public class StringDialog extends StandardDialog implements ActionListener, WindowListener { static final boolean debug = false; /** * <p>TranslationService object for handling string localization in * the Ganymede server.</p> */ static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.JDialog.StringDialog"); static final String ok = ts.l("global.ok"); // "Ok" static final String cancel = ts.l("global.cancel"); // "Cancel" /** * Returns the localized Ok string for the language used by the * client's Java environment. */ public static String getDefaultOk() { return ok; } /** * Returns the localized Cancel string for the language used by the * client's Java environment. */ public static String getDefaultCancel() { return cancel; } // -- DialogRsrc resource = null; Hashtable componentHash, valueHash; boolean done; JLabel imageCanvas; JButton OKButton, CancelButton; JPanel panel, mainPanel, dataPanel, buttonPanel; JLabel textLabel; GridBagLayout gbl, compgbl; GridBagConstraints gbc, compgbc; Image image; Vector objects; Vector components; /* -- */ /** * * Simple constructor for a small dialog box * * @param frame Parent frame of the Dialog Box * @param Title Title of the Dialog Box * @param Text Text shown in the Dialog Box * @param ShowCancel if true, show a "Cancel" button * */ public StringDialog(Frame frame, String Title, String Text, boolean ShowCancel, StandardDialog.ModalityType modality) { this(frame, Title, Text, ts.l("global.ok"), ShowCancel ? ts.l("global.cancel") : null, null, modality); } /** * * Simple constructor for a small dialog box with a Cancel button * * @param frame Parent frame of the Dialog Box * @param Title Title of the Dialog Box * @param Text Text shown in the Dialog Box * */ public StringDialog(Frame frame, String Title, String Text, StandardDialog.ModalityType modality) { this(frame, Title, Text, ts.l("global.ok"), ts.l("global.cancel"), null, modality); } /** * Simple constructor for a small dialog box * * @param frame Parent frame of the Dialog Box * @param Title Title of the Dialog Box * @param Text Text shown in the Dialog Box * @param OK String for "OK" button * @param Cancel String for "Cancel" button * */ public StringDialog(Frame frame, String Title, String Text, String OK, String Cancel, StandardDialog.ModalityType modality) { this(new DialogRsrc(frame, Title, Text, OK, Cancel, (Image) null), modality); } /** * Simple constructor for a small dialog box * * @param frame Parent frame of the Dialog Box * @param Title Title of the Dialog Box * @param Text Text shown in the Dialog Box * @param OK String for "OK" button * @param Cancel String for "Cancel" button * @param image Image to display next to text */ public StringDialog(Frame frame, String Title, String Text, String OK, String Cancel, Image image, StandardDialog.ModalityType modality) { this(new DialogRsrc(frame, Title, Text, OK, Cancel, image), modality); } /** * * Constructor for more complicated StringDialog. * * @param Resource Sets resource for Dialog box. * */ public StringDialog(DialogRsrc Resource, StandardDialog.ModalityType modality) { super(Resource.frame, Resource.title, modality); this.resource = Resource; create(); } public Dimension getPreferredSize() { // Let's make sure we don't try to get too big by default. Dimension size = super.getPreferredSize(); if (size.width > 1000) { size.width = 1000; } if (size.height > 600) { size.height = 600; } return size; } private void create() { this.addWindowListener(this); if (debug) { System.err.println("StringDialog constructor"); } mainPanel = new JPanel(); mainPanel.setBorder(new CompoundBorder(new EtchedBorder(), new EmptyBorder(10, 10, 10, 10))); mainPanel.setLayout(new BorderLayout()); setContentPane(mainPanel); // // Title at top of dialog, if we're a sheet on a Mac // if (isMacSheet()) { JLabel titleLabel = new JLabel(resource.title == null ? "": resource.title, SwingConstants.CENTER); mainPanel.add(titleLabel, BorderLayout.PAGE_START); } // // Image on left hand side // image = resource.getImage(); JPanel imagePanel = new JPanel(); if (image != null) { imageCanvas = new JLabel(new ImageIcon(image)); imagePanel.add(imageCanvas); } else { imagePanel.add(Box.createGlue()); } mainPanel.add(imagePanel, BorderLayout.LINE_START); if (resource.getText() != null && !resource.getText().trim().equals("")) { String resourceText = resource.getText(); resourceText = "<html>" + resourceText.replace("\n", "<br>") + "</html>"; textLabel = new JLabel(resourceText); JScrollPane pane = new JScrollPane(textLabel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); pane.getViewport().setOpaque(false); pane.setViewportBorder(null); pane.setBorder(new EmptyBorder(10,10,10,10)); mainPanel.add(pane, "Center"); } // now we need to create our south panel JPanel southPanel = new JPanel(); southPanel.setLayout(new BorderLayout()); mainPanel.add(southPanel, BorderLayout.PAGE_END); // // Now to build the south panel // // Our data object panel // panel = createElementPanel(); southPanel.add(panel, BorderLayout.CENTER); // // ButtonPanel takes up the bottom of the dialog // buttonPanel = new JPanel(); buttonPanel.setLayout(new BorderLayout()); JPanel flowPanel = new JPanel(); OKButton = new JButton(resource.getOkText()); OKButton.addActionListener(this); // if cancel is null, don't put it on there CancelButton = null; if (resource.getCancelText() != null) { CancelButton = new JButton(resource.getCancelText()); CancelButton.addActionListener(this); flowPanel.add(CancelButton); } if (isRunningOnMac()) { // On the Mac, we place the Cancel button to the left of the Ok // button. if (CancelButton != null) { flowPanel.add(CancelButton); } flowPanel.add(OKButton); } else { // otherwise, Ok is to the left. flowPanel.add(OKButton); if (CancelButton != null) { flowPanel.add(CancelButton); } } buttonPanel.add(new JSeparator(), "North"); if (isRunningOnMac()) { JPanel macPanel = new JPanel(); macPanel.setLayout(new BorderLayout()); macPanel.add(flowPanel, BorderLayout.LINE_END); // right align buttonPanel.add(macPanel, BorderLayout.PAGE_END); } else { buttonPanel.add(flowPanel, BorderLayout.PAGE_END); } southPanel.add(buttonPanel, BorderLayout.PAGE_END); registerCallbacks(); pack(); } private JPanel createElementPanel() { JPanel panel = new JPanel(); panel.setBorder(null); compgbc = new GridBagConstraints(); compgbl = new GridBagLayout(); compgbc.insets = new Insets(0,4,0,4); panel.setLayout(compgbl); componentHash = new Hashtable(); valueHash = new Hashtable(); objects = resource.getObjects(); components = new Vector(objects.size()); int numberOfObjects = objects.size(); if (numberOfObjects == 0) { if (debug) { System.err.println("no fields to add to StringDialog"); } return panel; } for (int i = 0; i < numberOfObjects; ++i) { Object element = objects.elementAt(i); if (debug) { System.err.println("number: " + numberOfObjects + " current: " + i); } if (element instanceof stringThing) { if (debug) { System.err.println("Adding string field(JstringField)"); } stringThing st = (stringThing) element; if (st.isMultiline()) { JstringArea sa = new JstringArea(5, 40); sa.setText(st.getValue()); sa.setEditable(true); addRow(panel, sa, st.getLabel(), i); componentHash.put(sa, st.getLabel()); valueHash.put(st.getLabel(), ""); } else { JstringField sf = new JstringField(); sf.setText(st.getValue()); sf.setEditable(true); addRow(panel, sf, st.getLabel(), i); componentHash.put(sf, st.getLabel()); valueHash.put(st.getLabel(), ""); } } else if (element instanceof dateThing) { if (debug) { System.err.println("Adding date field(JcalendarField)"); } dateThing dt = (dateThing) element; JcalendarField dateField; Date currentDate; Date minDate = new Date(); if (dt.getDate() != null) { currentDate = dt.getDate(); if (dt.getMaxDate() != null) { if (currentDate.after(dt.getMaxDate())) { currentDate = dt.getMaxDate(); } } } else { if (dt.getMaxDate() != null) { currentDate = dt.getMaxDate(); } else { throw new NullPointerException(); } } if (dt.getMaxDate() != null) { dateField = new JcalendarField(currentDate, true, true, minDate, dt.getMaxDate()); } else { dateField = new JcalendarField(currentDate, true, false, null, null); } addRow(panel, dateField, dt.getLabel(), i); componentHash.put(dateField, dt.getLabel()); valueHash.put(dt.getLabel(), currentDate); } else if (element instanceof passwordThing) { if (debug) { System.err.println("Adding password field(JpasswordField)"); } passwordThing pt = (passwordThing)element; // new password, so we're not trying to validate against // an existing password.. we'll want to give the user a // double password field so that they can validate if (pt.isNew()) { if (debug) { System.err.println("This password is new."); } JpassField sf = new JpassField(null,10,100,true); addRow(panel, sf, pt.getLabel(), i); componentHash.put(sf, pt.getLabel()); } else { if (debug) { System.err.println("This password is not new."); } JpasswordField sf = new JpasswordField(); sf.setEditable(true); addRow(panel, sf, pt.getLabel(), i); componentHash.put(sf, pt.getLabel()); } valueHash.put(pt.getLabel(), ""); } else if (element instanceof booleanThing) { if (debug) { System.err.println("Adding boolean field (JcheckboxField)"); } booleanThing bt = (booleanThing)element; JCheckBox cb = new JCheckBox(); cb.setSelected(bt.getValue()); addRow(panel, cb, bt.getLabel(), i); componentHash.put(cb, bt.getLabel()); valueHash.put(bt.getLabel(), Boolean.valueOf(bt.getValue())); } else if (element instanceof choiceThing) { if (debug) { System.err.println("Adding choice field (JComboBox)"); } choiceThing ct = (choiceThing)element; JComboBox ch = new JComboBox(); ch.setKeySelectionManager(new TimedKeySelectionManager()); if (debug) { System.err.println("Getting choice lists"); } Vector items = ct.getItems(); if (debug) { System.err.println("Got choice lists"); } if (items == null) { if (debug) { System.err.println("Nothing to add to Choice, empty vector"); } } else { int total = items.size(); for (int j = 0; j < total ; ++j) { ch.addItem(items.elementAt(j)); } if (ct.getSelectedItem() != null) { if (!items.contains(ct.getSelectedItem())) { ch.addItem(ct.getSelectedItem()); } ch.setSelectedItem(ct.getSelectedItem()); } addRow(panel, ch, ct.getLabel(), i); componentHash.put(ch, ct.getLabel()); if (ch.getSelectedItem() != null) { valueHash.put(ct.getLabel(), ch.getSelectedItem()); } } } else { System.err.println("StringDialog constructor: Item " + i + " is of unknown type"); } } return panel; } /** * * We want to make it so that when the user hits enter on the last * string or password field in the dialog, the ok button is clicked. * */ protected void registerCallbacks() { for (int i = 0; i < components.size(); i++) { JComponent c = (JComponent)components.elementAt(i); if (i == 0) { // not sure if this does us any good on X Windows, as // focus is usually managed by clicking or rolling the // mouse on to the dialog window.. might help on Win32. c.setRequestFocusEnabled(true); c.requestFocus(); } if (debug) { System.err.println("Checking component: " + c); } if (c instanceof JstringField) { JstringField sf = (JstringField) c; if (i == components.size() -1) // last one! { sf.addActionListener ( new ActionListener() { public void actionPerformed(ActionEvent e) { OKButton.doClick(); } }); } else { sf.addActionListener ( new ActionListener() { public void actionPerformed(ActionEvent e) { JComponent thisComp = (JComponent)e.getSource(); ((JComponent)components.elementAt(components.indexOf(thisComp) + 1)).requestFocus(); } }); } } else if (c instanceof JpasswordField) { if (debug) { System.err.println("This is a JpasswordField, number " + i); } JpasswordField pf = (JpasswordField) c; if (i == components.size() -1) { pf.addActionListener ( new ActionListener() { public void actionPerformed(ActionEvent e) { OKButton.doClick(); } }); } else { pf.addActionListener ( new ActionListener() { public void actionPerformed(ActionEvent e) { JComponent thisComp = (JComponent)e.getSource(); ((JComponent)components.elementAt(components.indexOf(thisComp) + 1)).requestFocus(); } }); } } } } /** * <p>Set the word wrap length of the main text area in this StringDialog.</p> */ public void setWrapLength(int wrapLength) { //textLabel.setWrapLength(wrapLength); } public void setFont(Font font) { if (textLabel != null) { textLabel.setFont(font); } } /** * <p>Display the dialog box, locks this thread while the dialog is being * displayed, and returns a hashtable of data field values when the * user closes the dialog box.</p> * * <p>Use this instead of Dialog.show(). If Hashtable returned is null, * then the cancel button was clicked. Otherwise, it will contain a * hash of labels(String) to results (Object).</p> * * @return HashTable of labels to values */ public Hashtable showDialog() { mainPanel.revalidate(); this.pack(); this.setVisible(true); // at this point we're frozen, since we're a modal dialog.. we'll continue // at this point when the ok or cancel buttons are pressed. if (debug) { System.err.println("Done invoking."); } return valueHash; } /** * * Handle the ok and cancel buttons. * */ public synchronized void actionPerformed(ActionEvent e) { if (e.getSource() == OKButton) { loadValueHash(); } else { valueHash = null; } // pop down so that showDialog() can proceed to completion. done = true; setVisible(false); } /** * * This method is responsible for scanning all of the input fields * in this dialog and loading their values into valueHash for * showDialog() to return. * */ private void loadValueHash() { for (int i = 0; i < components.size(); i++) { JComponent c = (JComponent)components.elementAt(i); String label = (String) componentHash.get(c); if (debug) { System.err.println("Loading value for field: " + label); } try { if (c instanceof JstringField) { JstringField sf = (JstringField) c; valueHash.put(label, sf.getText()); } else if (c instanceof JstringArea) { JstringArea sA = (JstringArea) c; valueHash.put(label, sA.getText()); } else if (c instanceof JpasswordField) { JpasswordField pf = (JpasswordField) c; valueHash.put(label, pf.getPassword()); } else if (c instanceof JpassField) { JpassField pf = (JpassField) c; valueHash.put(label, pf.getPassword()); } else if (c instanceof JcalendarField) { JcalendarField dF = (JcalendarField) c; valueHash.put(label, dF.getDate()); } else if (c instanceof JCheckBox) { JCheckBox cb = (JCheckBox) c; valueHash.put(label, Boolean.valueOf(cb.isSelected())); } else if (c instanceof JComboBox) { JComboBox combo = (JComboBox) c; valueHash.put(label, combo.getSelectedItem()); } } catch (NullPointerException ex) { } } } /** * * Convenience method to add a GUI component to this dialog. * */ private final void addRow(JPanel parent, JComponent comp, String label, int row) { components.addElement(comp); compgbc.gridwidth = 1; compgbc.fill = GridBagConstraints.NONE; compgbc.anchor = GridBagConstraints.WEST; compgbc.gridy = row; compgbc.gridx = 0; compgbc.weightx = 0.0; JLabel l = new JLabel(label, SwingConstants.LEFT); compgbl.setConstraints(l, compgbc); parent.add(l); compgbc.gridx = 1; compgbc.weightx = 1.0; compgbc.fill = GridBagConstraints.HORIZONTAL; compgbl.setConstraints(comp, compgbc); parent.add(comp); parent.invalidate(); } // WindowListener methods public void windowActivated(WindowEvent event) { } public void windowClosed(WindowEvent event) { } public synchronized void windowClosing(WindowEvent event) { if (!done) { if (debug) { System.err.println("Window is closing and we haven't done a cancel."); } // by setting valueHash to null, we're basically treating // this window close as a cancel. valueHash = null; } done = true; this.setVisible(false); } public void windowDeactivated(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowOpened(WindowEvent event) { } }