1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: OptionsField.java 2092 2011-03-07 12:20:11Z dmsmith $
21   */
22  package org.crosswire.common.config.swing;
23  
24  import java.awt.Dimension;
25  import java.util.Map;
26  
27  import javax.swing.DefaultComboBoxModel;
28  import javax.swing.JComboBox;
29  import javax.swing.JComponent;
30  
31  import org.crosswire.common.config.Choice;
32  import org.crosswire.common.config.MultipleChoice;
33  import org.crosswire.common.diff.Distance;
34  import org.crosswire.common.swing.GuiUtil;
35  import org.crosswire.common.swing.CWOtherMsg;
36  import org.crosswire.common.util.Logger;
37  
38  /**
39   * Allow the user to choose from a combo box.
40   * 
41   * @see gnu.lgpl.License for license details.<br>
42   *      The copyright to this program is held by it's authors.
43   * @author Joe Walker [joe at eireneh dot com]
44   */
45  public class OptionsField implements Field {
46  
47      /**
48       * Create an empty OptionsField
49       */
50      public OptionsField() {
51          combo = new JComboBox(new String[] {
52                  CWOtherMsg.lookupText("No Options Set")
53          });
54          // Set the preferred width. Note, the actual combo box will resize to
55          // the width of it's container
56          combo.setPreferredSize(new Dimension(100, combo.getPreferredSize().height));
57          GuiUtil.applyDefaultOrientation(combo);
58      }
59  
60      /**
61       * Some fields will need some extra info to display properly like the
62       * options in an options field. FieldMap calls this method with options
63       * provided by the choice.
64       * 
65       * @param param
66       *            The options provided by the Choice
67       */
68      public void setChoice(Choice param) {
69          if (param instanceof MultipleChoice) {
70              MultipleChoice mc = (MultipleChoice) param;
71              list = mc.getOptions();
72  
73              if (list == null) {
74                  throw new IllegalArgumentException("getOptions() returns null for option with help text " + mc.getHelpText());
75              }
76              combo.setModel(new DefaultComboBoxModel(list));
77          } else {
78              list = new String[] {
79                      CWOtherMsg.lookupText("Error")
80              };
81          }
82      }
83  
84      /**
85       * Return a string for use in the properties file
86       * 
87       * @return The current value
88       */
89      public String getValue() {
90          Object reply = combo.getSelectedItem();
91  
92          if (reply instanceof Map.Entry<?, ?>) {
93              return ((Map.Entry<?, ?>) reply).getKey().toString();
94          }
95          return reply == null ? "" : reply.toString();
96      }
97  
98      /**
99       * Set the current value
100      * 
101      * @param value
102      *            The new text
103      */
104     public void setValue(String value) {
105         if (list != null && list.length > 0) {
106             int distance = value.length();
107             for (int i = 0; i < list.length; i++) {
108                 if (value.equals(list[i])) {
109                     combo.setSelectedItem(list[i]);
110                     return;
111                 }
112                 distance = Math.max(distance, list[i].length());
113             }
114 
115             // We didn't find an exact match so look for the closest one.
116             distance++; // A number larger than the length of any of the strings
117             // in question.
118             int bestMatch = 0;
119             for (int i = 0; i < list.length; i++) {
120                 int newDistance = Distance.getLevenshteinDistance(value, list[i]);
121                 if (distance > newDistance) {
122                     bestMatch = i;
123                     distance = newDistance;
124                 }
125             }
126 
127             combo.setSelectedItem(list[bestMatch]);
128             if (bestMatch > 0) {
129                 log.warn("Checked for options without finding exact match: '" + value + "'. Best match is: " + combo.getSelectedItem());
130                 return;
131             }
132         }
133 
134         // Equate null and empty string
135         Object selected = combo.getSelectedItem();
136         if (value.length() > 0 && selected != null) {
137             log.warn("Checked for options without finding: '" + value + "'. Defaulting to first option: " + selected);
138         }
139     }
140 
141     /**
142      * Get the actual component that we can add to a Panel. (This can well be
143      * this in an implementation).
144      */
145     public JComponent getComponent() {
146         return combo;
147     }
148 
149     /**
150      * The component that we are wrapping in a field
151      */
152     private JComboBox combo;
153 
154     /**
155      * The options
156      */
157     private String[] list;
158 
159     /**
160      * The log stream
161      */
162     private static final Logger log = Logger.getLogger(OptionsField.class);
163 }
164