View Javadoc

1   package nl.tudelft.goal.ut2004.visualizer.gui.widgets;
2   
3   import java.awt.Component;
4   import java.awt.event.ActionListener;
5   import java.awt.event.KeyAdapter;
6   import java.awt.event.KeyEvent;
7   import java.util.List;
8   
9   import javax.swing.ComboBoxEditor;
10  import javax.swing.JComboBox;
11  import javax.swing.JTextField;
12  import javax.swing.SwingUtilities;
13  
14  class SuggestionFieldEditor implements ComboBoxEditor {
15  
16  	private final JTextField textField;
17  	private final SuggestionModel model;
18  	private int nextSuggestionIndex = 0;
19  	private final JComboBox combobox;
20  
21  	public SuggestionFieldEditor(JComboBox combobox, SuggestionModel model) {
22  		this.textField = new JTextField();
23  		this.model = model;
24  		this.combobox = combobox;
25  		this.textField.addKeyListener(new Listener());
26  	}
27  
28  	private class Listener extends KeyAdapter {
29  
30  		@Override
31  		public void keyPressed(final KeyEvent e) {
32  			// Invoke later to update after the key event has actually been done.
33  			SwingUtilities.invokeLater(new Runnable() {
34  				@Override
35  				public void run() {
36  					updateSuggestion(isAutoCompletionRequest(e));
37  				}
38  			});
39  		}
40  	}
41  
42  	private boolean isAutoCompletionRequest(KeyEvent e) {
43  		return e.isControlDown() && e.getKeyChar() == ' ';
44  	}
45  
46  	private void updateSuggestion(boolean complete) {
47  
48  		// Hide popup, it will change.
49  		combobox.hidePopup();
50  
51  		String query = getUnselectedText();
52  
53  		// Get suggestion from provider, should take care to
54  		// update model to only show new suggestions.
55  		List<String> suggestions = model.sugestFor(query);
56  
57  		// Only complete on request if we have results
58  		if (suggestions.size() > 0
59  		// And only if the user isn't deleting text
60  		// Or wants a completion.
61  				&& (isQueryEdited(query) || complete)) {
62  			nextSuggestionIndex %= suggestions.size();
63  			String suggestion = suggestions.get(nextSuggestionIndex++);
64  
65  			updateCompletion(complete, query, suggestion);
66  
67  		} else {
68  			// Reset counter if we did not provide a suggestion.
69  			nextSuggestionIndex = 0;
70  		}
71  
72  		// Show popup box with trimmed list of suggestions
73  		// as promised by the provider.
74  		combobox.showPopup();
75  	}
76  
77  	private String previousQuery = null;
78  
79  	/**
80  	 * Checks if the user tries to change the suggestion.
81  	 * 
82  	 * A suggestion is changed iff query.startsWith(previousQuery) && query.lenght() > previousQuery.lenght().
83  	 */
84  	private boolean isQueryEdited(String query) {
85  		boolean edited = previousQuery != null
86  				&& query.length() > previousQuery.length()
87  				&& query.startsWith(previousQuery);
88  
89  		previousQuery = query;
90  		return edited;
91  	}
92  
93  	/**
94  	 * Updates the text field with the suggestion. Ensures that if the suggestion is a completion it gets appended and highlighted. Unless the user requested
95  	 * the suggestion to be shown.
96  	 * 
97  	 * @param showSuggestion
98  	 *            iff the user requested to see the selection.
99  	 * @param query
100 	 *            the query made by the user.
101 	 * @param suggestion
102 	 *            the suggestion that matches the query.
103 	 */
104 	private void updateCompletion(boolean showSuggestion, String query,
105 			String suggestion) {
106 
107 		// We complete either because we were asked to
108 		if (showSuggestion
109 		// Or because we have a sensible completion
110 				|| (suggestion.startsWith(query) && query.length() > 0)) {
111 
112 			textField.setText(suggestion);
113 
114 			// If we completed because we have a completion mark it
115 			// as such.
116 			if (suggestion.startsWith(query)) {
117 				textField.select(query.length(), suggestion.length());
118 			}
119 			// Otherwise make it a suggestion.
120 			else {
121 				textField.select(suggestion.indexOf(query) + query.length(),
122 						suggestion.length());
123 			}
124 		}
125 	}
126 
127 	/**
128 	 * Returns the section of the text that the user provided as input.
129 	 * 
130 	 * This method assumes the user has accepted the completion when it is no longer highlighted until the last character.
131 	 * 
132 	 * @return the original text the user put in.
133 	 */
134 	private String getUnselectedText() {
135 
136 		String selected = textField.getSelectedText();
137 		String fullText = textField.getText();
138 
139 		// Does the selection match that of a completion?
140 		if (selected != null && fullText.endsWith(selected)) {
141 			return fullText.substring(0, fullText.indexOf(selected));
142 		}
143 
144 		// User accepted suggestion.
145 		return fullText;
146 	}
147 
148 	@Override
149 	public Component getEditorComponent() {
150 		return textField;
151 	}
152 
153 	@Override
154 	public Object getItem() {
155 		return model.getItem(textField.getText());
156 	}
157 
158 	@Override
159 	public void removeActionListener(ActionListener l) {
160 		textField.removeActionListener(l);
161 	}
162 
163 	@Override
164 	public void addActionListener(ActionListener l) {
165 		textField.addActionListener(l);
166 
167 	}
168 
169 	@Override
170 	public void selectAll() {
171 		textField.selectAll();
172 	}
173 
174 	@Override
175 	public void setItem(Object item) {
176 		if (item != null) {
177 			textField.setText(item.toString());
178 		} else {
179 			textField.setText("");
180 		}
181 	}
182 
183 }