001: package org.drools.brms.client.common;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import com.google.gwt.user.client.DOM;
020: import com.google.gwt.user.client.Event;
021: import com.google.gwt.user.client.ui.KeyboardListener;
022: import com.google.gwt.user.client.ui.ListBox;
023: import com.google.gwt.user.client.ui.PopupPanel;
024: import com.google.gwt.user.client.ui.RootPanel;
025: import com.google.gwt.user.client.ui.TextBox;
026: import com.google.gwt.user.client.ui.Widget;
027:
028: /**
029: * This nifty utility provides auto completion. Drop in replacement for a text box.
030: * I recall lifting it from somewhere on http://del.icio.us/michaelneale/GWT, plus some
031: * small tweaks.
032: *
033: * If this gives any back chat, I will shut it down and we can just use a regular text box.
034: *
035: */
036: public class AutoCompleteTextBoxAsync extends TextBox implements
037: KeyboardListener {
038:
039: protected CompletionItemsAsync items = null;
040: protected boolean popupAdded = false;
041: protected boolean visible = false;
042: protected PopupPanel choicesPopup = new PopupPanel(true);
043: protected ListBox choices = new ListBox() {
044: public void onBrowserEvent(Event event) {
045: if (Event.ONCLICK == DOM.eventGetType(event)) {
046: complete();
047: }
048: }
049: };
050:
051: /**
052: * Default Constructor
053: */
054: public AutoCompleteTextBoxAsync(CompletionItemsAsync comp) {
055: super ();
056: this .addKeyboardListener(this );
057: choices.sinkEvents(Event.ONCLICK);
058: this .setStyleName("AutoCompleteTextBox");
059:
060: choicesPopup.add(choices);
061: choicesPopup.addStyleName("AutoCompleteChoices");
062:
063: choices.setStyleName("list");
064: this .items = comp;
065: }
066:
067: /**
068: * Sets an "algorithm" returning completion items
069: * You can define your own way how the textbox retrieves
070: autocompletion items
071: * by implementing the CompletionItems interface and setting the
072: according object
073: * @see SimpleAutoCompletionItem
074: * @param items CompletionItem implementation
075: */
076: public void setCompletionItems(CompletionItemsAsync items) {
077: this .items = items;
078: }
079:
080: /**
081: * Returns the used CompletionItems object
082: * @return CompletionItems implementation
083: */
084: public CompletionItemsAsync getCompletionItems() {
085: return this .items;
086: }
087:
088: /**
089: * Handle events that happen when keys are pressed.
090: */
091: public void onKeyDown(Widget arg0, char arg1, int arg2) {
092: if (arg1 == KEY_ENTER) {
093: enterKey(arg0, arg1, arg2);
094: } else if (arg1 == KEY_TAB) {
095: tabKey(arg0, arg1, arg2);
096: } else if (arg1 == KEY_DOWN) {
097: downKey(arg0, arg1, arg2);
098: } else if (arg1 == KEY_UP) {
099: upKey(arg0, arg1, arg2);
100: } else if (arg1 == KEY_ESCAPE) {
101: escapeKey(arg0, arg1, arg2);
102: }
103: }
104:
105: /**
106: * Not used at all
107: */
108: public void onKeyPress(Widget arg0, char arg1, int arg2) {
109: }
110:
111: /**
112: * Handle events that happen when keys are released.
113: */
114: public void onKeyUp(Widget arg0, char arg1, int arg2) {
115: switch (arg1) {
116: case KEY_ALT:
117: case KEY_CTRL:
118: case KEY_DOWN:
119: case KEY_END:
120: case KEY_ENTER:
121: case KEY_ESCAPE:
122: case KEY_HOME:
123: case KEY_LEFT:
124: case KEY_PAGEDOWN:
125: case KEY_PAGEUP:
126: case KEY_RIGHT:
127: case KEY_SHIFT:
128: case KEY_TAB:
129: case KEY_UP:
130: break;
131: default:
132: otherKey(arg0, arg1, arg2);
133: break;
134: }
135: }
136:
137: // The down key was pressed.
138: protected void downKey(Widget arg0, char arg1, int arg2) {
139: int selectedIndex = choices.getSelectedIndex();
140: selectedIndex++;
141: if (selectedIndex >= choices.getItemCount()) {
142: selectedIndex = 0;
143: }
144: choices.setSelectedIndex(selectedIndex);
145: }
146:
147: // The up key was pressed.
148: protected void upKey(Widget arg0, char arg1, int arg2) {
149: int selectedIndex = choices.getSelectedIndex();
150: selectedIndex--;
151: if (selectedIndex < 0) {
152: selectedIndex = choices.getItemCount() - 1;
153: }
154: choices.setSelectedIndex(selectedIndex);
155: }
156:
157: // The enter key was pressed.
158: protected void enterKey(Widget arg0, char arg1, int arg2) {
159: complete();
160: }
161:
162: // The tab key was pressed.
163: protected void tabKey(Widget arg0, char arg1, int arg2) {
164: complete();
165: }
166:
167: // The escape key was pressed.
168: protected void escapeKey(Widget arg0, char arg1, int arg2) {
169: choices.clear();
170: choicesPopup.hide();
171: this .visible = false;
172: }
173:
174: // Any other non-special key was pressed.
175: protected void otherKey(Widget arg0, char arg1, int arg2) {
176: // Update the existing choices in the list box to reflect the user's entry.
177: updateChoices(this .getText());
178:
179: // If any text was entered, start an async callback.
180: if (this .getText().length() > 0 && items != null) {
181: items.getCompletionItems(this .getText(),
182: new CompletionItemsAsyncReturn() {
183: public void itemReturn(String[] matches) {
184: updateChoices(matches, getText());
185: }
186: });
187: }
188: }
189:
190: // Hides/shows the choice box as needed.
191: // Assumes all choices are currently valid.
192: protected void hideChoicesIfNeeded(String text) {
193: // Hide the list box under any of these conditions:
194: // - the text box is empty
195: // - there are no matching choices
196: // - there is only one choice that exactly matches the text box entry
197: // Show the list box under any other condition.
198: if (0 == text.length()
199: || 0 == choices.getItemCount()
200: || (1 == choices.getItemCount() && choices.getItemText(
201: 0).equals(text))) {
202: choices.clear();
203: choicesPopup.hide();
204: visible = false;
205: } else {
206: choices.setSelectedIndex(0);
207: choices.setVisibleItemCount(choices.getItemCount() + 1);
208:
209: if (!popupAdded) {
210: RootPanel.get().add(choicesPopup);
211: popupAdded = true;
212: }
213: choicesPopup.show();
214: visible = true;
215: choicesPopup.setPopupPosition(this .getAbsoluteLeft(), this
216: .getAbsoluteTop()
217: + this .getOffsetHeight());
218: choices.setWidth(this .getOffsetWidth() + "px");
219: }
220: }
221:
222: // Removes all items in the choices menu that do not start with the specified text.
223: protected void updateChoices(String text) {
224: int i = 0;
225: while (i < choices.getItemCount()) {
226: if (choices.getItemText(i).toLowerCase().startsWith(
227: text.toLowerCase())) {
228: ++i;
229: } else {
230: choices.removeItem(i);
231: }
232: }
233: hideChoicesIfNeeded(text);
234: }
235:
236: // Update the choices menu using the provided matches and entered text.
237: protected void updateChoices(String[] matches, String text) {
238: choices.clear();
239: for (int i = 0; i < matches.length; i++) {
240: choices.addItem((String) matches[i]);
241: }
242: updateChoices(text);
243: }
244:
245: // add selected item to textbox
246: protected void complete() {
247: if (this .visible && choices.getItemCount() > 0) {
248: this .setText(choices
249: .getItemText(choices.getSelectedIndex()));
250: }
251: choices.clear();
252: choicesPopup.hide();
253: this .visible = false;
254: }
255:
256: }
|