Ticket #163: auto-complete.2

File auto-complete.2, 7.1 KB (added by guilhem.bonnefille@…, 18 years ago)

auto-complete refactored

Line 
1Add auto-completion to key/value comboboxes
2
3From: <>
4
5
6---
7
8 .../josm/gui/dialogs/AutoCompleteComboBox.java | 96 +++++++++++++++++++++++
9 .../josm/gui/dialogs/PropertiesDialog.java | 41 +++++-----
10 2 files changed, 118 insertions(+), 19 deletions(-)
11
12diff --git a/src/org/openstreetmap/josm/gui/dialogs/AutoCompleteComboBox.java b/src/org/openstreetmap/josm/gui/dialogs/AutoCompleteComboBox.java
13new file mode 100644
14index 0000000..aec3d5f
15--- /dev/null
16+++ b/src/org/openstreetmap/josm/gui/dialogs/AutoCompleteComboBox.java
17@@ -0,0 +1,96 @@
18+package org.openstreetmap.josm.gui.dialogs;
19+
20+import java.util.Collection;
21+
22+import javax.swing.ComboBoxModel;
23+import javax.swing.DefaultComboBoxModel;
24+import javax.swing.JComboBox;
25+import javax.swing.text.AttributeSet;
26+import javax.swing.text.BadLocationException;
27+import javax.swing.text.JTextComponent;
28+import javax.swing.text.PlainDocument;
29+
30+public class AutoCompleteComboBox extends JComboBox {
31+
32+ /**
33+ * Auto-complete a JComboBox.
34+ *
35+ * Inspired by http://www.orbital-computer.de/JComboBox/
36+ */
37+ protected class AutoCompleteComboBoxDocument extends PlainDocument {
38+ private JComboBox comboBox;
39+
40+ private boolean selecting = false;
41+
42+ public AutoCompleteComboBoxDocument(final JComboBox comboBox) {
43+ this.comboBox = comboBox;
44+ }
45+
46+ public void remove(int offs, int len) throws BadLocationException {
47+ // return immediately when selecting an item
48+ if (selecting)
49+ return;
50+ super.remove(offs, len);
51+ }
52+
53+ public void insertString(int offs, String str, AttributeSet a)
54+ throws BadLocationException {
55+ // insert the string into the document
56+ super.insertString(offs, str, a);
57+ // return immediately when selecting an item
58+ // Nota: this is done after calling super method because we need
59+ // ActionListener informed
60+ if (selecting)
61+ return;
62+ // lookup and select a matching item
63+ Object item = lookupItem(getText(0, getLength()));
64+ if (item != null) {
65+ // remove all text and insert the completed string
66+ super.remove(0, getLength());
67+ super.insertString(0, item.toString(), a);
68+ // select the completed part
69+ JTextComponent editor = (JTextComponent)comboBox.getEditor()
70+ .getEditorComponent();
71+ editor.setSelectionStart(offs + str.length());
72+ editor.setSelectionEnd(getLength());
73+ }
74+ setSelectedItem(item);
75+ }
76+
77+ private void setSelectedItem(Object item) {
78+ selecting = true;
79+ comboBox.setSelectedItem(item);
80+ selecting = false;
81+ }
82+
83+ private Object lookupItem(String pattern) {
84+ // iterate over all items
85+ ComboBoxModel model = comboBox.getModel();
86+ for (int i = 0, n = model.getSize(); i < n; i++) {
87+ Object currentItem = model.getElementAt(i);
88+ // current item starts with the pattern?
89+ if (currentItem.toString().startsWith(pattern)) {
90+ return currentItem;
91+ }
92+ }
93+ // no item starts with the pattern => return null
94+ return null;
95+ }
96+ }
97+
98+ AutoCompleteComboBox() {
99+ // get the combo box' editor component
100+ JTextComponent editor = (JTextComponent) this.getEditor().getEditorComponent();
101+ // change the editor's document
102+ editor.setDocument(new AutoCompleteComboBoxDocument(this));
103+ }
104+
105+ public void setPossibleItems(Collection<String> elems) {
106+ Object oldValue = this.getSelectedItem();
107+ DefaultComboBoxModel model = (DefaultComboBoxModel)this.getModel();
108+ model.removeAllElements();
109+ for (String elem : elems) model.addElement(elem);
110+ this.setSelectedItem(oldValue);
111+ this.getEditor().selectAll();
112+ }
113+}
114diff --git a/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java b/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
115index da639d9..a8e1349 100644
116--- a/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
117+++ b/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
118@@ -11,6 +11,8 @@ import java.awt.GridBagLayout;
119 import java.awt.GridLayout;
120 import java.awt.event.ActionEvent;
121 import java.awt.event.ActionListener;
122+import java.awt.event.FocusAdapter;
123+import java.awt.event.FocusEvent;
124 import java.awt.event.KeyEvent;
125 import java.awt.event.MouseAdapter;
126 import java.awt.event.MouseEvent;
127@@ -36,6 +38,7 @@ import javax.swing.JTextField;
128 import javax.swing.ListSelectionModel;
129 import javax.swing.table.DefaultTableCellRenderer;
130 import javax.swing.table.DefaultTableModel;
131+import javax.swing.text.JTextComponent;
132
133 import org.openstreetmap.josm.Main;
134 import org.openstreetmap.josm.command.ChangePropertyCommand;
135@@ -164,8 +167,8 @@ public class PropertiesDialog extends ToggleDialog implements SelectionChangedLi
136 }
137
138 /**
139- * Open the add selection dialog and add a new key/value to the table (and to the
140- * dataset, of course).
141+ * Open the add selection dialog and add a new key/value to the table (and
142+ * to the dataset, of course).
143 */
144 void add() {
145 Collection<OsmPrimitive> sel = Main.ds.getSelected();
146@@ -192,33 +195,33 @@ public class PropertiesDialog extends ToggleDialog implements SelectionChangedLi
147 }
148 for (int i = 0; i < data.getRowCount(); ++i)
149 allData.remove(data.getValueAt(i, 0));
150- final JComboBox keys = new JComboBox(new Vector<String>(allData.keySet()));
151+ final AutoCompleteComboBox keys = new AutoCompleteComboBox();
152+ keys.setPossibleItems(allData.keySet());
153 keys.setEditable(true);
154+
155 p.add(keys, BorderLayout.CENTER);
156
157 JPanel p2 = new JPanel(new BorderLayout());
158 p.add(p2, BorderLayout.SOUTH);
159 p2.add(new JLabel(tr("Please select a value")), BorderLayout.NORTH);
160- final JComboBox values = new JComboBox();
161+ final AutoCompleteComboBox values = new AutoCompleteComboBox();
162 values.setEditable(true);
163 p2.add(values, BorderLayout.CENTER);
164-
165- ActionListener link = new ActionListener() {
166-
167- public void actionPerformed(ActionEvent e) {
168- String key = keys.getEditor().getItem().toString();
169- if (allData.containsKey(key)) {
170- Vector<String> newValues = new Vector<String>(allData.get(key));
171- Object oldValue = values.getSelectedItem();
172- values.setModel(new DefaultComboBoxModel(newValues));
173- values.setSelectedItem(oldValue);
174- values.getEditor().selectAll();
175+
176+ // get the combo box' editor component
177+ JTextComponent editor = (JTextComponent) values.getEditor().getEditorComponent();
178+ // Refresh the values model when focus is gained
179+ editor.addFocusListener(new FocusAdapter() {
180+ public void focusGained(FocusEvent e) {
181+ String key = keys.getEditor().getItem().toString();
182+ if (allData.containsKey(key)) {
183+ values.setPossibleItems(allData.get(key));
184+ } else {
185+ values.removeAllItems();
186 }
187 }
188-
189- };
190- keys.addActionListener(link);
191-
192+ });
193+
194 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
195 @Override public void selectInitialValue() {
196 keys.requestFocusInWindow();