View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  package net.sf.magicproject.ui;
20  
21  import java.awt.event.ActionEvent;
22  import java.awt.event.ActionListener;
23  import java.awt.event.KeyAdapter;
24  import java.awt.event.KeyEvent;
25  import java.awt.event.MouseEvent;
26  import java.awt.event.MouseListener;
27  
28  import javax.swing.JButton;
29  import javax.swing.JComponent;
30  import javax.swing.JList;
31  import javax.swing.JTextField;
32  import javax.swing.text.AttributeSet;
33  import javax.swing.text.BadLocationException;
34  import javax.swing.text.PlainDocument;
35  
36  import net.sf.magicproject.tools.MCardCompare;
37  
38  /***
39   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
40   * @since 0.2
41   */
42  public class HireListener extends PlainDocument implements ActionListener,
43  		MouseListener {
44  
45  	/***
46  	 * Creates a new instance of HireListener <br>
47  	 * 
48  	 * @param editor
49  	 *          the textField
50  	 * @param hireComponent
51  	 *          is component to enable/disable when an element is [de]selected
52  	 * @param toRefresh
53  	 * @param mappedList
54  	 *          the list to synchronize with the content of the textfield.
55  	 */
56  	public HireListener(final JTextField editor, JButton hireComponent,
57  			RefreshableAdd toRefresh, JList mappedList) {
58  		this.hireComponent = hireComponent;
59  		this.toRefresh = toRefresh;
60  		this.mappedList = mappedList;
61  		this.editor = editor;
62  		editor.addActionListener(this);
63  		editor.setDocument(this);
64  		editor.addMouseListener(this);
65  		hireComponent.addActionListener(this);
66  
67  		editor.addKeyListener(new KeyAdapter() {
68  			@Override
69  			public void keyPressed(KeyEvent e) {
70  				hitBackspace = false;
71  				// determine if the pressed key is backspace (needed by the remove
72  				// method)
73  				switch (e.getKeyCode()) {
74  				case KeyEvent.VK_BACK_SPACE:
75  					hitBackspace = true;
76  					hitBackspaceOnSelection = editor.getSelectionStart() != editor
77  							.getSelectionEnd();
78  					break;
79  				// ignore delete key
80  				case KeyEvent.VK_DELETE:
81  					e.consume();
82  					break;
83  				default:
84  					// continue
85  				}
86  			}
87  		});
88  	}
89  
90  	public void actionPerformed(ActionEvent e) {
91  		toRefresh.refreshAddComponent(false);
92  	}
93  
94  	@Override
95  	public void remove(int offs, int len) throws BadLocationException {
96  		// return immediately when selecting an item
97  		if (selecting)
98  			return;
99  		if (hitBackspace) {
100 			// user hit backspace => move the selection backwards
101 			int workinOffs = offs;
102 			// old item keeps being selected
103 			if (workinOffs > 0) {
104 				if (hitBackspaceOnSelection)
105 					workinOffs--;
106 			} else {
107 				// User hit backspace with the cursor positioned on the start => beep
108 				// comboBox.getToolkit().beep(); // when available use:
109 				// UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
110 			}
111 			highlightCompletedText(workinOffs);
112 		} else {
113 			super.remove(offs, len);
114 		}
115 	}
116 
117 	@Override
118 	public void insertString(int offs, String str, AttributeSet a)
119 			throws BadLocationException {
120 		// return immediately when selecting an item
121 		if (selecting)
122 			return;
123 		// insert the string into the document
124 		int workingOffs = offs;
125 		super.insertString(workingOffs, str, a);
126 		// lookup and select a matching item
127 		Object item = updateSelectedValue(getText(0, getLength()));
128 		if (item == null) {
129 			// keep old item selected if there is no match
130 			item = this.mappedList.getSelectedValue();
131 			workingOffs = workingOffs - str.length();
132 		}
133 		setText(item.toString());
134 		// select the completed part
135 		highlightCompletedText(workingOffs + str.length());
136 	}
137 
138 	private void setText(String text) {
139 		try {
140 			// remove all text and insert the completed string
141 			super.remove(0, getLength());
142 			super.insertString(0, text, null);
143 		} catch (BadLocationException e) {
144 			throw new RuntimeException(e.toString());
145 		}
146 	}
147 
148 	private void highlightCompletedText(int start) {
149 		editor.setCaretPosition(getLength());
150 		editor.moveCaretPosition(start);
151 	}
152 
153 	/***
154 	 * @param select
155 	 *          the text to find.
156 	 * @return the selected value.
157 	 */
158 	private Object updateSelectedValue(String pattern) {
159 
160 		// iterate over all items
161 		if (pattern.length() == 0) {
162 			hireComponent.setEnabled(false);
163 		} else {
164 			try {
165 				final String selectedItem;
166 				if (mappedList.getSelectedValue() == null)
167 					selectedItem = null;
168 				else
169 					selectedItem = mappedList.getSelectedValue().toString().toLowerCase();
170 				String workingText = pattern.toLowerCase();
171 				String currentText = getText(0, getLength()).toLowerCase();
172 				// search for a different item only if the currently selected does not
173 				// match
174 				if (selectedItem != null && selectedItem.startsWith(pattern)) {
175 					return mappedList.getSelectedValue();
176 				}
177 
178 				MListModel<MCardCompare> model = ((MListModel<MCardCompare>) mappedList
179 						.getModel());
180 				for (int index = 0, n = model.getSize(); index < n; index++) {
181 					String value = model.getElementAt(index).toString().toLowerCase();
182 					if (value.startsWith(workingText)) {
183 						mappedList.setSelectedIndex(index);
184 						mappedList.ensureIndexIsVisible(index);
185 						remove(0, currentText.length());
186 						super.insertString(0, model.get(index).getName(), null);
187 						editor.setCaretPosition(value.length());
188 						editor.moveCaretPosition(workingText.length());
189 						return model.getElementAt(index);
190 					}
191 				}
192 			} catch (BadLocationException e1) {
193 				// Ignore this error
194 			}
195 		}
196 		return mappedList.getSelectedValue();
197 	}
198 
199 	public void mouseClicked(MouseEvent e) {
200 		if (e.getSource() == editor) {
201 			editor.selectAll();
202 		}
203 	}
204 
205 	public void mouseEntered(MouseEvent e) {
206 		// unhandled
207 		return;
208 	}
209 
210 	public void mouseExited(MouseEvent e) {
211 		// unhandled
212 		return;
213 	}
214 
215 	public void mousePressed(MouseEvent e) {
216 		// unhandled
217 		return;
218 	}
219 
220 	public void mouseReleased(MouseEvent e) {
221 		// unhandled
222 		return;
223 	}
224 
225 	/***
226 	 * Hire component used with this HireListener
227 	 */
228 	private JComponent hireComponent;
229 
230 	/***
231 	 * The component to refresh on active selection of an element of this list or
232 	 * on clic on the given JButton.
233 	 */
234 	private final RefreshableAdd toRefresh;
235 
236 	private final JList mappedList;
237 
238 	private final JTextField editor;
239 
240 	boolean hitBackspace = false;
241 
242 	boolean hitBackspaceOnSelection;
243 
244 	boolean selecting = false;
245 
246 }