Ticket #163: auto-complete

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

auto-completion

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