View Javadoc
1   package org.vaadin.aceeditor.client;
2   
3   import java.util.LinkedList;
4   import java.util.List;
5   
6   import com.google.gwt.core.client.GWT;
7   import com.google.gwt.event.dom.client.ChangeEvent;
8   import com.google.gwt.event.dom.client.ChangeHandler;
9   import com.google.gwt.event.dom.client.DoubleClickEvent;
10  import com.google.gwt.event.dom.client.DoubleClickHandler;
11  import com.google.gwt.event.dom.client.KeyCodes;
12  import com.google.gwt.event.dom.client.KeyDownEvent;
13  import com.google.gwt.event.dom.client.KeyDownHandler;
14  import com.google.gwt.user.client.Event;
15  import com.google.gwt.user.client.ui.HTML;
16  import com.google.gwt.user.client.ui.Image;
17  import com.google.gwt.user.client.ui.ListBox;
18  import com.vaadin.client.ui.VOverlay;
19  
20  public class SuggestPopup extends VOverlay implements KeyDownHandler,
21  		DoubleClickHandler, ChangeHandler {
22  	protected ListBox choiceList;
23  
24      protected String startOfValue = "";
25  
26  	public interface SuggestionSelectedListener {
27  		void suggestionSelected(TransportSuggestion s);
28  		void noSuggestionSelected();
29  	}
30  
31      protected SuggestionSelectedListener listener;
32  
33      protected VOverlay descriptionPopup;
34  
35      protected List<TransportSuggestion> suggs;
36      protected List<TransportSuggestion> visibleSuggs = new LinkedList<TransportSuggestion>();
37  
38      protected boolean showDescriptions = true;
39  
40      protected Image loadingImage;
41  
42  	public static final int WIDTH = 150;
43  	public static final int HEIGHT = 200;
44  
45  	public static final int DESCRIPTION_WIDTH = 225;
46  
47  	// TODO addSuggestionSelectedListener?
48  	public void setSuggestionSelectedListener(SuggestionSelectedListener ssl) {
49  		listener = ssl;
50  	}
51  
52  	public SuggestPopup() {
53  		super(true);
54  		setWidth(WIDTH + "px");
55  		SuggestionResources resources = GWT.create(SuggestionResources.class);
56  		loadingImage = new Image(resources.loading());
57  		setWidget(loadingImage);
58  	}
59  	
60  	protected void createChoiceList() {
61  		choiceList = new ListBox();
62  		choiceList.setStyleName("list");
63  		choiceList.addKeyDownHandler(this);
64  		choiceList.addDoubleClickHandler(this);
65  		choiceList.addChangeHandler(this);
66  		choiceList.setStylePrimaryName("aceeditor-suggestpopup-list");
67  		setWidget(choiceList);
68  	}
69  	
70  	protected void startLoading() {
71  		if (descriptionPopup!=null) {
72  			descriptionPopup.hide();
73  		}
74  		setWidget(loadingImage);
75  	}
76  	
77  	public void setSuggestions(List<TransportSuggestion> suggs) {
78  		this.suggs = suggs;
79  		createChoiceList();
80  		populateList();
81  		if (choiceList.getItemCount() == 0) {
82  			close();
83  		}
84  	}
85  
86      protected void populateList() {
87  		choiceList.clear();
88  		visibleSuggs.clear();
89  		int i = 0;
90  		for (TransportSuggestion s : suggs) {
91  			if (s.suggestionText.toLowerCase().startsWith(startOfValue)) {
92  				visibleSuggs.add(s);
93  				choiceList.addItem(s.displayText, "" + i);
94  			}
95  			i++;
96  		}
97  		if (choiceList.getItemCount() > 0) {
98  			int vic = Math.max(2, Math.min(10, choiceList.getItemCount()));
99  			choiceList.setVisibleItemCount(vic);
100 			choiceList.setSelectedIndex(0);
101 			this.onChange(null);
102 		}
103 	}
104 
105 	public void close() {
106 		hide();
107 		if (listener != null)
108 			listener.noSuggestionSelected();
109 	}
110 
111 	@Override
112 	public void hide() {
113 		super.hide();
114 		if (descriptionPopup != null)
115 			descriptionPopup.hide();
116 		descriptionPopup = null;
117 	}
118 
119 	@Override
120 	public void hide(boolean ac) {
121 		super.hide(ac);
122 		if (ac) {
123 			// This happens when user clicks outside this popup (or something
124 			// similar) while autohide is on. We must cancel the suggestion.
125 			if (listener != null)
126 				listener.noSuggestionSelected();
127 		}
128 		if (descriptionPopup != null)
129 			descriptionPopup.hide();
130 		descriptionPopup = null;
131 	}
132 
133 	/* @Override */
134 	public void onKeyDown(KeyDownEvent event) {
135 		int keyCode = event.getNativeKeyCode();
136 		if (keyCode == KeyCodes.KEY_ENTER
137 				&& choiceList.getSelectedIndex() != -1) {
138 			event.preventDefault();
139 			event.stopPropagation();
140 			select();
141 		} else if (keyCode == KeyCodes.KEY_ESCAPE) {
142 			event.preventDefault();
143 			close();
144 		}
145 	}
146 
147 	/* @Override */
148 	public void onDoubleClick(DoubleClickEvent event) {
149 		event.preventDefault();
150 		event.stopPropagation();
151 		select();
152 	}
153 
154 	@Override
155 	public void onBrowserEvent(Event event) {
156 		if (event.getTypeInt() == Event.ONCONTEXTMENU) {
157 			event.stopPropagation();
158 			event.preventDefault();
159 			return;
160 		}
161 		super.onBrowserEvent(event);
162 	}
163 
164 	public void up() {
165 		if (suggs==null) {
166 			return;
167 		}
168 		int current = this.choiceList.getSelectedIndex();
169 		int next = (current - 1 >= 0) ? current - 1 : 0;
170 		this.choiceList.setSelectedIndex(next);
171 		// Note that setting the selection programmatically does not cause the
172 		// ChangeHandler.onChange(ChangeEvent) event to be fired.
173 		// Doing it manually.
174 		this.onChange(null);
175 	}
176 
177 	public void down() {
178 		if (suggs==null) {
179 			return;
180 		}
181 		int current = this.choiceList.getSelectedIndex();
182 		int next = (current + 1 < choiceList.getItemCount()) ? current + 1
183 				: current;
184 		this.choiceList.setSelectedIndex(next);
185 		// Note that setting the selection programmatically does not cause the
186 		// ChangeHandler.onChange(ChangeEvent) event to be fired.
187 		// Doing it manually.
188 		this.onChange(null);
189 	}
190 
191 	public void select() {
192 		if (suggs==null) {
193 			return;
194 		}
195 		
196 		int selected = choiceList.getSelectedIndex();
197 		if (listener != null) {
198 			if (selected == -1) {
199 				this.hide();
200 				listener.noSuggestionSelected();
201 			} else {
202 				startLoading();
203 				listener.suggestionSelected(visibleSuggs.get(selected));
204 			}
205 		}
206 	}
207 
208 	@Override
209 	public void onChange(ChangeEvent event) {
210 		if (descriptionPopup == null) {
211 			createDescriptionPopup();
212 		}
213 
214 		int selected = choiceList.getSelectedIndex();
215 		String descr = visibleSuggs.get(selected).descriptionText;
216 
217 		if (descr != null && !descr.isEmpty()) {
218 			((HTML) descriptionPopup.getWidget()).setHTML(descr);
219             if (showDescriptions) {
220                 descriptionPopup.show();
221             }
222         } else {
223 			descriptionPopup.hide();
224 		}
225 	}
226 
227 	@Override
228 	public void setPopupPosition(int left, int top) {
229 		super.setPopupPosition(left, top);
230 		if (descriptionPopup!=null) {
231 			updateDescriptionPopupPosition();
232 		}
233 	}
234 	
235 	protected void updateDescriptionPopupPosition() {
236 		int x = getAbsoluteLeft() + WIDTH;
237 		int y = getAbsoluteTop();
238 		descriptionPopup.setPopupPosition(x, y);
239 		if (descriptionPopup!=null) {
240 			descriptionPopup.setPopupPosition(x, y);
241 		}
242 	}
243 
244 	protected void createDescriptionPopup() {
245 		descriptionPopup = new VOverlay();
246 		descriptionPopup.setOwner(getOwner());
247 		descriptionPopup.setStylePrimaryName("aceeditor-suggestpopup-description");
248 		HTML lbl = new HTML();
249 		lbl.setWordWrap(true);
250 		descriptionPopup.setWidget(lbl);
251 		updateDescriptionPopupPosition();
252 		descriptionPopup.setWidth(DESCRIPTION_WIDTH+"px");
253 //		descriptionPopup.setSize(DESCRIPTION_WIDTH+"px", HEIGHT+"px");
254 	}
255 
256 	public void setStartOfValue(String startOfValue) {
257 		this.startOfValue = startOfValue.toLowerCase();
258 		if (suggs==null) {
259 			return;
260 		}
261 		populateList();
262 		if (choiceList.getItemCount() == 0) {
263 			close();
264 		}
265 	}
266 
267 }