View Javadoc
1   package org.argeo.connect.ui.widgets;
2   
3   import java.util.Arrays;
4   import java.util.List;
5   
6   import org.argeo.cms.util.CmsUtils;
7   import org.argeo.eclipse.ui.EclipseUiUtils;
8   import org.eclipse.rap.rwt.widgets.DropDown;
9   import org.eclipse.swt.SWT;
10  import org.eclipse.swt.events.FocusEvent;
11  import org.eclipse.swt.events.FocusListener;
12  import org.eclipse.swt.widgets.Event;
13  import org.eclipse.swt.widgets.Listener;
14  import org.eclipse.swt.widgets.Text;
15  import org.eclipse.swt.widgets.Widget;
16  
17  /**
18   * Enable easy addition of a {@code DropDown} widget to a text with listeners
19   * configured
20   */
21  public abstract class ConnectAbstractDropDown {
22  
23  	private final Text text;
24  	private final DropDown dropDown;
25  	private boolean modifyFromList = false;
26  
27  	// Current displayed text
28  	private String userText = "";
29  	// Current displayed list items
30  	private String[] values;
31  
32  	// Fine tuning
33  	boolean readOnly;
34  	boolean refreshOnFocus;
35  
36  	/** Implementing classes should call refreshValues() after initialisation */
37  	public ConnectAbstractDropDown(Text text) {
38  		this(text, SWT.NONE, false);
39  	}
40  
41  	/**
42  	 * Implementing classes should call refreshValues() after initialisation
43  	 * 
44  	 * @param text
45  	 * @param style
46  	 *            only SWT.READ_ONLY is understood, check if the entered text is
47  	 *            part of the legal choices.
48  	 */
49  	public ConnectAbstractDropDown(Text text, int style) {
50  		this(text, style, false);
51  	}
52  
53  	/**
54  	 * Implementers should call refreshValues() once init has been done.
55  	 * 
56  	 * @param text
57  	 * @param style
58  	 *            only SWT.READ_ONLY is understood, check if the entered text is
59  	 *            part of the legal choices.
60  	 * @param refreshOnFocus
61  	 *            if true, the possible values are computed each time the focus is
62  	 *            gained. It enables, among other to fine tune the getFilteredValues
63  	 *            method depending on the current context
64  	 */
65  	public ConnectAbstractDropDown(Text text, int style, boolean refreshOnFocus) {
66  		this.text = text;
67  		dropDown = new DropDown(text);
68  		Object obj = dropDown;
69  		if (obj instanceof Widget)
70  			CmsUtils.markup((Widget) obj);
71  		readOnly = (style & SWT.READ_ONLY) != 0;
72  		this.refreshOnFocus = refreshOnFocus;
73  		addListeners();
74  	}
75  
76  	/**
77  	 * Overwrite to force the refresh of the possible values on focus gained event
78  	 */
79  	protected boolean refreshOnFocus() {
80  		return refreshOnFocus;
81  	}
82  
83  	public String getText() {
84  		return text.getText();
85  	}
86  
87  	public void init() {
88  		refreshValues();
89  	}
90  
91  	public void reset(String value) {
92  		modifyFromList = true;
93  		if (EclipseUiUtils.notEmpty(value))
94  			text.setText(value);
95  		else
96  			text.setText("");
97  		refreshValues();
98  		modifyFromList = false;
99  	}
100 
101 	/** Overwrite to provide specific filtering */
102 	protected abstract List<String> getFilteredValues(String filter);
103 
104 	protected void refreshValues() {
105 		List<String> filteredValues = getFilteredValues(text.getText());
106 		values = filteredValues.toArray(new String[filteredValues.size()]);
107 		dropDown.setItems(values);
108 	}
109 
110 	protected void addListeners() {
111 		addModifyListener();
112 		addSelectionListener();
113 		addDefaultSelectionListener();
114 		addFocusListener();
115 	}
116 
117 	protected void addFocusListener() {
118 		text.addFocusListener(new FocusListener() {
119 			private static final long serialVersionUID = -7179112097626535946L;
120 
121 			public void focusGained(FocusEvent event) {
122 				if (refreshOnFocus) {
123 					modifyFromList = true;
124 					refreshValues();
125 					modifyFromList = false;
126 				}
127 				dropDown.setVisible(true);
128 			}
129 
130 			public void focusLost(FocusEvent event) {
131 				dropDown.setVisible(false);
132 				if (readOnly && values != null && !Arrays.asList(values).contains(userText)) {
133 					modifyFromList = true;
134 					text.setText("");
135 					refreshValues();
136 					modifyFromList = false;
137 				}
138 			}
139 		});
140 	}
141 
142 	private void addSelectionListener() {
143 		Object obj = dropDown;
144 		if (obj instanceof Widget)
145 			((Widget) obj).addListener(SWT.Selection, new Listener() {
146 				private static final long serialVersionUID = -2357157809365135142L;
147 
148 				public void handleEvent(Event event) {
149 					if (event.index != -1) {
150 						modifyFromList = true;
151 						text.setText(values[event.index]);
152 						modifyFromList = false;
153 						text.selectAll();
154 					} else {
155 						text.setText(userText);
156 						text.setSelection(userText.length(), userText.length());
157 						text.setFocus();
158 					}
159 				}
160 			});
161 	}
162 
163 	private void addDefaultSelectionListener() {
164 		Object obj = dropDown;
165 		if (obj instanceof Widget)
166 			((Widget) obj).addListener(SWT.DefaultSelection, new Listener() {
167 				private static final long serialVersionUID = -5958008322630466068L;
168 
169 				public void handleEvent(Event event) {
170 					if (event.index != -1) {
171 						text.setText(values[event.index]);
172 						text.setSelection(event.text.length());
173 						dropDown.setVisible(false);
174 					}
175 				}
176 			});
177 	}
178 
179 	private void addModifyListener() {
180 		text.addListener(SWT.Modify, new Listener() {
181 			private static final long serialVersionUID = -4373972835244263346L;
182 
183 			public void handleEvent(Event event) {
184 				if (!modifyFromList) {
185 					userText = text.getText();
186 					refreshValues();
187 					if (values.length == 1)
188 						dropDown.setSelectionIndex(0);
189 					dropDown.setVisible(true);
190 				}
191 			}
192 		});
193 	}
194 }