You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IFilterSelect.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import java.util.ArrayList;
  6. import java.util.Collection;
  7. import java.util.Date;
  8. import java.util.Iterator;
  9. import com.google.gwt.user.client.Command;
  10. import com.google.gwt.user.client.DOM;
  11. import com.google.gwt.user.client.Element;
  12. import com.google.gwt.user.client.Event;
  13. import com.google.gwt.user.client.Window;
  14. import com.google.gwt.user.client.ui.ClickListener;
  15. import com.google.gwt.user.client.ui.Composite;
  16. import com.google.gwt.user.client.ui.FlowPanel;
  17. import com.google.gwt.user.client.ui.FocusListener;
  18. import com.google.gwt.user.client.ui.HTML;
  19. import com.google.gwt.user.client.ui.Image;
  20. import com.google.gwt.user.client.ui.KeyboardListener;
  21. import com.google.gwt.user.client.ui.PopupListener;
  22. import com.google.gwt.user.client.ui.PopupPanel;
  23. import com.google.gwt.user.client.ui.TextBox;
  24. import com.google.gwt.user.client.ui.Widget;
  25. import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
  26. import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
  27. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  28. import com.itmill.toolkit.terminal.gwt.client.Focusable;
  29. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  30. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  31. import com.itmill.toolkit.terminal.gwt.client.Util;
  32. /**
  33. *
  34. * TODO needs major refactoring (to be extensible etc)
  35. */
  36. public class IFilterSelect extends Composite implements Paintable, Field,
  37. KeyboardListener, ClickListener, FocusListener, Focusable {
  38. public class FilterSelectSuggestion implements Suggestion, Command {
  39. private final String key;
  40. private final String caption;
  41. private String iconUri;
  42. public FilterSelectSuggestion(UIDL uidl) {
  43. key = uidl.getStringAttribute("key");
  44. caption = uidl.getStringAttribute("caption");
  45. if (uidl.hasAttribute("icon")) {
  46. iconUri = client.translateToolkitUri(uidl
  47. .getStringAttribute("icon"));
  48. }
  49. }
  50. public String getDisplayString() {
  51. final StringBuffer sb = new StringBuffer();
  52. if (iconUri != null) {
  53. sb.append("<img src=\"");
  54. sb.append(iconUri);
  55. sb.append("\" alt=\"icon\" class=\"i-icon\" />");
  56. }
  57. sb.append(Util.escapeHTML(caption));
  58. return sb.toString();
  59. }
  60. public String getReplacementString() {
  61. return caption;
  62. }
  63. public int getOptionKey() {
  64. return Integer.parseInt(key);
  65. }
  66. public String getIconUri() {
  67. return iconUri;
  68. }
  69. public void execute() {
  70. onSuggestionSelected(this);
  71. }
  72. }
  73. /**
  74. * @author mattitahvonen
  75. *
  76. */
  77. public class SuggestionPopup extends ToolkitOverlay implements
  78. PositionCallback, PopupListener {
  79. private static final int EXTRASPACE = 8;
  80. private static final String Z_INDEX = "30000";
  81. private final SuggestionMenu menu;
  82. private final Element up = DOM.createDiv();
  83. private final Element down = DOM.createDiv();
  84. private final Element status = DOM.createDiv();
  85. private boolean isPagingEnabled = true;
  86. private long lastAutoClosed;
  87. SuggestionPopup() {
  88. super(true);
  89. menu = new SuggestionMenu();
  90. setWidget(menu);
  91. setStyleName(CLASSNAME + "-suggestpopup");
  92. DOM.setStyleAttribute(getElement(), "zIndex", Z_INDEX);
  93. final Element root = getContainerElement();
  94. DOM.setInnerHTML(up, "<span>Prev</span>");
  95. DOM.sinkEvents(up, Event.ONCLICK);
  96. DOM.setInnerHTML(down, "<span>Next</span>");
  97. DOM.sinkEvents(down, Event.ONCLICK);
  98. DOM.insertChild(root, up, 0);
  99. DOM.appendChild(root, down);
  100. DOM.appendChild(root, status);
  101. DOM.setElementProperty(status, "className", CLASSNAME + "-status");
  102. addPopupListener(this);
  103. }
  104. public void showSuggestions(Collection currentSuggestions,
  105. int currentPage, int totalSuggestions) {
  106. if (ApplicationConnection.isTestingMode()) {
  107. // Add TT anchor point
  108. DOM.setElementProperty(getElement(), "id", paintableId
  109. + "_OPTIONLIST");
  110. }
  111. menu.setSuggestions(currentSuggestions);
  112. final int x = IFilterSelect.this.getAbsoluteLeft();
  113. int y = tb.getAbsoluteTop();
  114. y += tb.getOffsetHeight();
  115. setPopupPosition(x, y);
  116. final int first = currentPage * PAGELENTH
  117. + (nullSelectionAllowed && currentPage > 0 ? 0 : 1);
  118. final int last = first + currentSuggestions.size() - 1;
  119. final int matches = totalSuggestions
  120. - (nullSelectionAllowed ? 1 : 0);
  121. if (last > 0) {
  122. // nullsel not counted, as requested by user
  123. DOM.setInnerText(status, (totalSuggestions == 0 ? 0 : first)
  124. + "-"
  125. + ("".equals(lastFilter) && nullSelectionAllowed
  126. && currentPage == 0 ? last - 1 : last) + "/"
  127. + matches);
  128. } else {
  129. DOM.setInnerText(status, "");
  130. }
  131. setPrevButtonActive(first > 1);
  132. setNextButtonActive(last < matches);
  133. // clear previously fixed width
  134. menu.setWidth("");
  135. DOM.setStyleAttribute(DOM.getFirstChild(menu.getElement()),
  136. "width", "");
  137. setPopupPositionAndShow(this);
  138. }
  139. private void setNextButtonActive(boolean b) {
  140. if (b) {
  141. DOM.sinkEvents(down, Event.ONCLICK);
  142. DOM.setElementProperty(down, "className", CLASSNAME
  143. + "-nextpage");
  144. } else {
  145. DOM.sinkEvents(down, 0);
  146. DOM.setElementProperty(down, "className", CLASSNAME
  147. + "-nextpage-off");
  148. }
  149. }
  150. private void setPrevButtonActive(boolean b) {
  151. if (b) {
  152. DOM.sinkEvents(up, Event.ONCLICK);
  153. DOM
  154. .setElementProperty(up, "className", CLASSNAME
  155. + "-prevpage");
  156. } else {
  157. DOM.sinkEvents(up, 0);
  158. DOM.setElementProperty(up, "className", CLASSNAME
  159. + "-prevpage-off");
  160. }
  161. }
  162. public void selectNextItem() {
  163. final MenuItem cur = menu.getSelectedItem();
  164. final int index = 1 + menu.getItems().indexOf(cur);
  165. if (menu.getItems().size() > index) {
  166. final MenuItem newSelectedItem = (MenuItem) menu.getItems()
  167. .get(index);
  168. menu.selectItem(newSelectedItem);
  169. tb.setText(newSelectedItem.getText());
  170. tb.setSelectionRange(lastFilter.length(), newSelectedItem
  171. .getText().length()
  172. - lastFilter.length());
  173. } else if (hasNextPage()) {
  174. filterOptions(currentPage + 1, lastFilter);
  175. }
  176. }
  177. public void selectPrevItem() {
  178. final MenuItem cur = menu.getSelectedItem();
  179. final int index = -1 + menu.getItems().indexOf(cur);
  180. if (index > -1) {
  181. final MenuItem newSelectedItem = (MenuItem) menu.getItems()
  182. .get(index);
  183. menu.selectItem(newSelectedItem);
  184. tb.setText(newSelectedItem.getText());
  185. tb.setSelectionRange(lastFilter.length(), newSelectedItem
  186. .getText().length()
  187. - lastFilter.length());
  188. } else if (index == -1) {
  189. if (currentPage > 0) {
  190. filterOptions(currentPage - 1, lastFilter);
  191. }
  192. } else {
  193. final MenuItem newSelectedItem = (MenuItem) menu.getItems()
  194. .get(menu.getItems().size() - 1);
  195. menu.selectItem(newSelectedItem);
  196. tb.setText(newSelectedItem.getText());
  197. tb.setSelectionRange(lastFilter.length(), newSelectedItem
  198. .getText().length()
  199. - lastFilter.length());
  200. }
  201. }
  202. public void onBrowserEvent(Event event) {
  203. final Element target = DOM.eventGetTarget(event);
  204. if (DOM.compare(target, up)
  205. || DOM.compare(target, DOM.getChild(up, 0))) {
  206. filterOptions(currentPage - 1, lastFilter);
  207. } else if (DOM.compare(target, down)
  208. || DOM.compare(target, DOM.getChild(down, 0))) {
  209. filterOptions(currentPage + 1, lastFilter);
  210. }
  211. tb.setFocus(true);
  212. }
  213. public void setPagingEnabled(boolean paging) {
  214. if (isPagingEnabled == paging) {
  215. return;
  216. }
  217. if (paging) {
  218. DOM.setStyleAttribute(down, "display", "block");
  219. DOM.setStyleAttribute(up, "display", "block");
  220. DOM.setStyleAttribute(status, "display", "block");
  221. } else {
  222. DOM.setStyleAttribute(down, "display", "none");
  223. DOM.setStyleAttribute(up, "display", "none");
  224. DOM.setStyleAttribute(status, "display", "none");
  225. }
  226. isPagingEnabled = paging;
  227. }
  228. /*
  229. * (non-Javadoc)
  230. *
  231. * @see com.google.gwt.user.client.ui.PopupPanel$PositionCallback#setPosition(int,
  232. * int)
  233. */
  234. public void setPosition(int offsetWidth, int offsetHeight) {
  235. int top = -1;
  236. int left = -1;
  237. // reset menu size and retrieve its "natural" size
  238. menu.setHeight("");
  239. if (currentPage > 0) {
  240. // fix height to avoid height change when getting to last page
  241. menu.fixHeightTo(PAGELENTH);
  242. }
  243. offsetHeight = getOffsetHeight();
  244. final int desiredWidth = IFilterSelect.this.getOffsetWidth();
  245. int naturalMenuWidth = DOM.getElementPropertyInt(DOM
  246. .getFirstChild(menu.getElement()), "offsetWidth");
  247. if (naturalMenuWidth < desiredWidth) {
  248. menu.setWidth(desiredWidth + "px");
  249. DOM.setStyleAttribute(DOM.getFirstChild(menu.getElement()),
  250. "width", "100%");
  251. naturalMenuWidth = desiredWidth;
  252. }
  253. if (Util.isIE()) {
  254. DOM.setStyleAttribute(getElement(), "width", naturalMenuWidth
  255. + "px");
  256. }
  257. if (offsetHeight + getPopupTop() > Window.getClientHeight()
  258. + Window.getScrollTop()) {
  259. // popup on top of input instead
  260. top = getPopupTop() - offsetHeight
  261. - IFilterSelect.this.getOffsetHeight();
  262. if (top < 0) {
  263. top = 0;
  264. }
  265. } else {
  266. top = getPopupTop();
  267. }
  268. // fetch real width (mac FF bugs here due GWT popups overflow:auto )
  269. offsetWidth = DOM.getElementPropertyInt(DOM.getFirstChild(menu
  270. .getElement()), "offsetWidth");
  271. if (offsetWidth + getPopupLeft() > Window.getClientWidth()
  272. + Window.getScrollLeft()) {
  273. left = IFilterSelect.this.getAbsoluteLeft()
  274. + IFilterSelect.this.getOffsetWidth()
  275. + Window.getScrollLeft() - offsetWidth;
  276. if (left < 0) {
  277. left = 0;
  278. }
  279. } else {
  280. left = getPopupLeft();
  281. }
  282. setPopupPosition(left, top);
  283. }
  284. /**
  285. * @return true if popup was just closed
  286. */
  287. public boolean isJustClosed() {
  288. final long now = (new Date()).getTime();
  289. return (lastAutoClosed > 0 && (now - lastAutoClosed) < 200);
  290. }
  291. public void onPopupClosed(PopupPanel sender, boolean autoClosed) {
  292. if (autoClosed) {
  293. lastAutoClosed = (new Date()).getTime();
  294. }
  295. }
  296. }
  297. public class SuggestionMenu extends MenuBar {
  298. SuggestionMenu() {
  299. super(true);
  300. setStyleName(CLASSNAME + "-suggestmenu");
  301. }
  302. /**
  303. * Fixes menus height to use same space as full page would use. Needed
  304. * to avoid height changes when quickly "scrolling" to last page
  305. */
  306. public void fixHeightTo(int pagelenth) {
  307. if (currentSuggestions.size() > 0) {
  308. final int pixels = pagelenth * (getOffsetHeight() - 2)
  309. / currentSuggestions.size();
  310. setHeight((pixels + 2) + "px");
  311. }
  312. }
  313. public void setSuggestions(Collection suggestions) {
  314. clearItems();
  315. final Iterator it = suggestions.iterator();
  316. while (it.hasNext()) {
  317. final FilterSelectSuggestion s = (FilterSelectSuggestion) it
  318. .next();
  319. final MenuItem mi = new MenuItem(s.getDisplayString(), true, s);
  320. this.addItem(mi);
  321. if (s == currentSuggestion) {
  322. selectItem(mi);
  323. }
  324. }
  325. }
  326. public void doSelectedItemAction() {
  327. final MenuItem item = getSelectedItem();
  328. final String enteredItemValue = tb.getText();
  329. // check for exact match in menu
  330. int p = getItems().size();
  331. if (p > 0) {
  332. for (int i = 0; i < p; i++) {
  333. final MenuItem potentialExactMatch = (MenuItem) getItems()
  334. .get(i);
  335. if (potentialExactMatch.getText().equals(enteredItemValue)) {
  336. selectItem(potentialExactMatch);
  337. doItemAction(potentialExactMatch, true);
  338. suggestionPopup.hide();
  339. return;
  340. }
  341. }
  342. }
  343. if (allowNewItem) {
  344. if (!enteredItemValue.equals(emptyText)) {
  345. client.updateVariable(paintableId, "newitem",
  346. enteredItemValue, immediate);
  347. }
  348. } else if (item != null
  349. && item.getText().toLowerCase().startsWith(
  350. lastFilter.toLowerCase())) {
  351. doItemAction(item, true);
  352. } else {
  353. if (currentSuggestion != null) {
  354. String text = currentSuggestion.getReplacementString();
  355. tb.setText((text.equals("") ? emptyText : text));
  356. // TODO add/remove class CLASSNAME_EMPTY
  357. selectedOptionKey = currentSuggestion.key;
  358. } else {
  359. tb.setText(emptyText);
  360. // TODO add class CLASSNAME_EMPTY
  361. selectedOptionKey = null;
  362. }
  363. }
  364. suggestionPopup.hide();
  365. }
  366. }
  367. public static final int FILTERINGMODE_OFF = 0;
  368. public static final int FILTERINGMODE_STARTSWITH = 1;
  369. public static final int FILTERINGMODE_CONTAINS = 2;
  370. private static final String CLASSNAME = "i-filterselect";
  371. public static final int PAGELENTH = 10;
  372. private final FlowPanel panel = new FlowPanel();
  373. private final TextBox tb = new TextBox();
  374. private final SuggestionPopup suggestionPopup = new SuggestionPopup();
  375. private final HTML popupOpener = new HTML("");
  376. private final Image selectedItemIcon = new Image();
  377. private ApplicationConnection client;
  378. private String paintableId;
  379. private int currentPage;
  380. private final Collection currentSuggestions = new ArrayList();
  381. private boolean immediate;
  382. private String selectedOptionKey;
  383. private boolean filtering = false;
  384. private String lastFilter = "";
  385. private FilterSelectSuggestion currentSuggestion;
  386. private int totalMatches;
  387. private boolean allowNewItem;
  388. private boolean nullSelectionAllowed;
  389. private boolean enabled;
  390. // shown in unfocused empty field, disappears on focus (e.g "Search here")
  391. private String emptyText = "";
  392. private static final String CLASSNAME_EMPTY = "empty";
  393. private static final String ATTR_EMPTYTEXT = "emptytext";
  394. public IFilterSelect() {
  395. selectedItemIcon.setVisible(false);
  396. panel.add(selectedItemIcon);
  397. panel.add(tb);
  398. panel.add(popupOpener);
  399. initWidget(panel);
  400. setStyleName(CLASSNAME);
  401. tb.addKeyboardListener(this);
  402. tb.setStyleName(CLASSNAME + "-input");
  403. tb.addFocusListener(this);
  404. popupOpener.setStyleName(CLASSNAME + "-button");
  405. popupOpener.addClickListener(this);
  406. }
  407. public boolean hasNextPage() {
  408. if (totalMatches > (currentPage + 1) * PAGELENTH) {
  409. return true;
  410. } else {
  411. return false;
  412. }
  413. }
  414. public void filterOptions(int page) {
  415. filterOptions(page, tb.getText());
  416. }
  417. public void filterOptions(int page, String filter) {
  418. if (filter.equals(lastFilter) && currentPage == page) {
  419. if (!suggestionPopup.isAttached()) {
  420. suggestionPopup.showSuggestions(currentSuggestions,
  421. currentPage, totalMatches);
  422. }
  423. return;
  424. }
  425. if (!filter.equals(lastFilter)) {
  426. // we are on subsequent page and text has changed -> reset page
  427. if ("".equals(filter)) {
  428. // let server decide
  429. page = -1;
  430. } else {
  431. page = 0;
  432. }
  433. }
  434. filtering = true;
  435. client.updateVariable(paintableId, "filter", filter, false);
  436. client.updateVariable(paintableId, "page", page, true);
  437. lastFilter = filter;
  438. currentPage = page;
  439. }
  440. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  441. paintableId = uidl.getId();
  442. this.client = client;
  443. if (uidl.hasAttribute("disabled") || uidl.hasAttribute("readonly")) {
  444. tb.setEnabled(false);
  445. enabled = false;
  446. } else {
  447. tb.setEnabled(true);
  448. enabled = true;
  449. }
  450. if (client.updateComponent(this, uidl, true)) {
  451. return;
  452. }
  453. immediate = uidl.hasAttribute("immediate");
  454. nullSelectionAllowed = uidl.hasAttribute("nullselect");
  455. currentPage = uidl.getIntVariable("page");
  456. if (uidl.hasAttribute(ATTR_EMPTYTEXT)) {
  457. // "emptytext" changed from server
  458. emptyText = uidl.getStringAttribute(ATTR_EMPTYTEXT);
  459. }
  460. suggestionPopup.setPagingEnabled(true);
  461. allowNewItem = uidl.hasAttribute("allownewitem");
  462. currentSuggestions.clear();
  463. final UIDL options = uidl.getChildUIDL(0);
  464. totalMatches = uidl.getIntAttribute("totalMatches");
  465. String captions = emptyText;
  466. for (final Iterator i = options.getChildIterator(); i.hasNext();) {
  467. final UIDL optionUidl = (UIDL) i.next();
  468. final FilterSelectSuggestion suggestion = new FilterSelectSuggestion(
  469. optionUidl);
  470. currentSuggestions.add(suggestion);
  471. if (optionUidl.hasAttribute("selected")) {
  472. if (!filtering) {
  473. tb.setText(suggestion.getReplacementString());
  474. }
  475. currentSuggestion = suggestion;
  476. }
  477. // Collect captions so we can calculate minimum width for textarea
  478. if (captions.length() > 0) {
  479. captions += "|";
  480. }
  481. captions += suggestion.getReplacementString();
  482. }
  483. if (!filtering && uidl.hasVariable("selected")
  484. && uidl.getStringArrayVariable("selected").length == 0) {
  485. // select nulled
  486. tb.setText(emptyText);
  487. selectedOptionKey = null;
  488. // TODO add class CLASSNAME_EMPTY
  489. }
  490. if (filtering
  491. && lastFilter.toLowerCase().equals(
  492. uidl.getStringVariable("filter"))) {
  493. suggestionPopup.showSuggestions(currentSuggestions, currentPage,
  494. totalMatches);
  495. filtering = false;
  496. }
  497. // Calculate minumum textarea width
  498. final int minw = minWidth(captions);
  499. final Element spacer = DOM.createDiv();
  500. DOM.setStyleAttribute(spacer, "width", minw + "px");
  501. DOM.setStyleAttribute(spacer, "height", "0");
  502. DOM.setStyleAttribute(spacer, "overflow", "hidden");
  503. DOM.appendChild(panel.getElement(), spacer);
  504. }
  505. public void onSuggestionSelected(FilterSelectSuggestion suggestion) {
  506. currentSuggestion = suggestion;
  507. String newKey;
  508. if (suggestion.key.equals("")) {
  509. // "nullselection"
  510. newKey = "";
  511. } else {
  512. // normal selection
  513. newKey = String.valueOf(suggestion.getOptionKey());
  514. }
  515. String text = suggestion.getReplacementString();
  516. tb.setText(text.equals("") ? emptyText : text);
  517. // TODO add/remove class CLASSNAME_EMPTY
  518. setSelectedItemIcon(suggestion.getIconUri());
  519. if (!newKey.equals(selectedOptionKey)) {
  520. selectedOptionKey = newKey;
  521. client.updateVariable(paintableId, "selected",
  522. new String[] { selectedOptionKey }, immediate);
  523. // currentPage = -1; // forget the page
  524. }
  525. suggestionPopup.hide();
  526. }
  527. private void setSelectedItemIcon(String iconUri) {
  528. if (iconUri == null) {
  529. selectedItemIcon.setVisible(false);
  530. } else {
  531. selectedItemIcon.setUrl(iconUri);
  532. selectedItemIcon.setVisible(true);
  533. }
  534. }
  535. public void onKeyDown(Widget sender, char keyCode, int modifiers) {
  536. if (enabled && suggestionPopup.isAttached()) {
  537. switch (keyCode) {
  538. case KeyboardListener.KEY_DOWN:
  539. suggestionPopup.selectNextItem();
  540. DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
  541. break;
  542. case KeyboardListener.KEY_UP:
  543. suggestionPopup.selectPrevItem();
  544. DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
  545. break;
  546. case KeyboardListener.KEY_PAGEDOWN:
  547. if (hasNextPage()) {
  548. filterOptions(currentPage + 1, lastFilter);
  549. }
  550. break;
  551. case KeyboardListener.KEY_PAGEUP:
  552. if (currentPage > 0) {
  553. filterOptions(currentPage - 1, lastFilter);
  554. }
  555. break;
  556. case KeyboardListener.KEY_ENTER:
  557. case KeyboardListener.KEY_TAB:
  558. suggestionPopup.menu.doSelectedItemAction();
  559. break;
  560. }
  561. }
  562. }
  563. public void onKeyPress(Widget sender, char keyCode, int modifiers) {
  564. }
  565. public void onKeyUp(Widget sender, char keyCode, int modifiers) {
  566. if (enabled) {
  567. switch (keyCode) {
  568. case KeyboardListener.KEY_ENTER:
  569. case KeyboardListener.KEY_TAB:
  570. ; // NOP
  571. break;
  572. case KeyboardListener.KEY_DOWN:
  573. case KeyboardListener.KEY_UP:
  574. case KeyboardListener.KEY_PAGEDOWN:
  575. case KeyboardListener.KEY_PAGEUP:
  576. if (suggestionPopup.isAttached()) {
  577. break;
  578. } else {
  579. // open popup as from gadget
  580. filterOptions(-1, "");
  581. lastFilter = "";
  582. tb.selectAll();
  583. break;
  584. }
  585. default:
  586. filterOptions(currentPage);
  587. break;
  588. }
  589. }
  590. }
  591. /**
  592. * Listener for popupopener
  593. */
  594. public void onClick(Widget sender) {
  595. if (enabled) {
  596. // ask suggestionPopup if it was just closed, we are using GWT
  597. // Popup's
  598. // auto close feature
  599. if (!suggestionPopup.isJustClosed()) {
  600. filterOptions(-1, "");
  601. lastFilter = "";
  602. }
  603. DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
  604. tb.setFocus(true);
  605. tb.selectAll();
  606. }
  607. }
  608. /*
  609. * Calculate minumum width for FilterSelect textarea
  610. */
  611. private native int minWidth(String captions)
  612. /*-{
  613. if(!captions || captions.length <= 0)
  614. return 0;
  615. captions = captions.split("|");
  616. var d = $wnd.document.createElement("div");
  617. var html = "";
  618. for(var i=0; i < captions.length; i++) {
  619. html += "<div>" + captions[i] + "</div>";
  620. // TODO apply same CSS classname as in suggestionmenu
  621. }
  622. d.style.position = "absolute";
  623. d.style.top = "0";
  624. d.style.left = "0";
  625. d.style.visibility = "hidden";
  626. d.innerHTML = html;
  627. $wnd.document.body.appendChild(d);
  628. var w = d.offsetWidth;
  629. $wnd.document.body.removeChild(d);
  630. return w;
  631. }-*/;
  632. public void onFocus(Widget sender) {
  633. if (emptyText.equals(tb.getText())) {
  634. tb.setText("");
  635. // TODO remove class CLASSNAME_EMPTY
  636. }
  637. }
  638. public void onLostFocus(Widget sender) {
  639. if (suggestionPopup.isJustClosed()) {
  640. suggestionPopup.menu.doSelectedItemAction();
  641. }
  642. if ("".equals(tb.getText())) {
  643. tb.setText(emptyText);
  644. // TODO add CLASSNAME_EMPTY
  645. }
  646. }
  647. public void focus() {
  648. tb.setFocus(true);
  649. }
  650. }