package org.gcube.portlets.widgets.pickitem.client.dialog; import java.util.ArrayList; import org.gcube.portlets.widgets.pickitem.client.bundle.CssAndImages; import org.gcube.portlets.widgets.pickitem.client.events.PickedUserEvent; import org.gcube.portlets.widgets.pickitem.client.uibinder.SelectableItem; import org.gcube.portlets.widgets.pickitem.client.uibinder.WithPhotoTemplate; import org.gcube.portlets.widgets.pickitem.shared.ItemBean; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.event.dom.client.MouseOverHandler; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.MultiWordSuggestOracle; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.SuggestOracle.Callback; import com.google.gwt.user.client.ui.SuggestOracle.Request; import com.google.gwt.user.client.ui.SuggestOracle.Response; import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; /** * * @author Massimiliano Assante, ISTI-CNR * Use this widget to display a a dropdown user list you can attach to a textbox to make select portal users typing @ * * To get to know which user was selected listen for the {@link PickedUserEvent} on the {@link HandlerManager} instance you pass to this widget. * */ public class PickItemsDialog extends PopupPanel { public final static int ARROW_UP = 38; public final static int ARROW_DOWN = 40; public final static int DELETE = KeyCodes.KEY_DELETE; public final static int ENTER = KeyCodes.KEY_ENTER; public final static int ESCAPE = KeyCodes.KEY_ESCAPE; public final static int TAB = KeyCodes.KEY_TAB; private HandlerManager eventBus; private int limit = 10; private final MultiWordSuggestOracle oracle = new MultiWordSuggestOracle(); private int displayIndexSelected; private FocusPanel focusPanel = new FocusPanel(); private VerticalPanel mainPanel = new VerticalPanel(); private String triggerChar; private ArrayList users; //needed because is selected when it popups private Widget first; static { CssAndImages.INSTANCE.css().ensureInjected(); } /** * @param triggerChar the 'single char' used to trigger the items list show, e.g. '@', '#' .... * @param the list of user to pick * @param eventBus the event bus on where the widget will fire the selected user event * @param widthInPixel the desired width (grater than 199 pixel) */ public PickItemsDialog(char triggerChar, ArrayList users, final HandlerManager eventBus, int widthInPixel) { super(true, false); if (widthInPixel < 200) { throw new IllegalArgumentException("width must be greater than 199"); } this.eventBus = eventBus; this.triggerChar = ""+triggerChar; this.users = users; focusPanel.setWidth(widthInPixel+"px"); mainPanel.setWidth(widthInPixel+"px"); setWidth(widthInPixel+"px"); focusPanel.add(mainPanel); setWidget(focusPanel); setStyleName("pickDialog"); //add the user fill names to the oracle for (ItemBean user : users) { oracle.add(user.getAlternativeName()); } //remove the first selected when hovering focusPanel.addMouseOverHandler(new MouseOverHandler() { @Override public void onMouseOver(MouseOverEvent event) { if (first != null) first.removeStyleName("pickperson-selected"); } }); focusPanel.addMouseOutHandler(new MouseOutHandler() { @Override public void onMouseOut(MouseOutEvent event) { select(displayIndexSelected); } }); focusPanel.addMouseDownHandler(new MouseDownHandler() { @Override public void onMouseDown(MouseDownEvent event) { SelectableItem ut = (SelectableItem) mainPanel.getWidget(displayIndexSelected); eventBus.fireEvent(new PickedUserEvent(new ItemBean("id", "username", ut.getItemName(), "thumb"))); hide(); select(0); //RESET } }); } /** * called for each keyUp event from the user * @param keyCode the event keycode * @param x * @param y * @param currText */ public void onKeyUp(int keyCode, int x, int y, String currText) { if (currText.endsWith(triggerChar)) { //the only way i found to intercept @ setPopupPosition(x, y); hide(); } else if (currText.contains(triggerChar)) { if (pickingUser(currText)) { handleNonCharKeys(keyCode); } } else if (!currText.contains(triggerChar)) hide(); } /** * split the text and keeps listening for user keyboard events * @param currText the text being typed */ private boolean pickingUser(String currText) { String[] toSplit = currText.split(triggerChar); //get the interesting part if (toSplit[1].trim().length() > 0) { showSuggestions(toSplit[1]); return true; } hide(); return false; } /** * handles the nonchar events (arrows, esc, enter etc) * @param event */ private void handleNonCharKeys(int keyCode) { switch (keyCode) { case ARROW_UP: if (displayIndexSelected > 0) select(--displayIndexSelected); break; case ARROW_DOWN: case TAB: if (displayIndexSelected+1 < mainPanel.getWidgetCount()) select(displayIndexSelected+1); break; case ESCAPE: case DELETE: hide(); case ENTER: //selectd with keyboard SelectableItem ut = null; if (mainPanel.getWidgetCount() > 0) { if (displayIndexSelected < 0 || displayIndexSelected >= mainPanel.getWidgetCount()) //when there's only one left sometimes here i get -sth, no time to see why :) ut = (SelectableItem) mainPanel.getWidget(0); else ut = (SelectableItem) mainPanel.getWidget(displayIndexSelected); eventBus.fireEvent(new PickedUserEvent(new ItemBean("id", "username", ut.getItemName(), "thumb"))); hide(); select(0); //RESET } break; default: break; } } public void showSuggestions(String query) { if (query.length() > 0) { oracle.requestSuggestions(new Request(query, limit), new Callback() { public void onSuggestionsReady(Request request, Response response) { mainPanel.clear(); int i = 0; for (Suggestion s : response.getSuggestions()) { if (i == 0) { first = getUserTemplate(getUserModelBySuggestion(s), i); first.addStyleName("pickperson-selected"); mainPanel.add(first); } else mainPanel.add(getUserTemplate(getUserModelBySuggestion(s), i)); i++; } if (i > 0) { show(); } } }); } } private ItemBean getUserModelBySuggestion(Suggestion suggestion) { for (ItemBean user : users) { if (suggestion.getReplacementString().compareTo(user.getAlternativeName()) ==0) return user; } return new ItemBean("no-match","no-match","no-match","no-match"); } private WithPhotoTemplate getUserTemplate(ItemBean user, int displayIndex) { return new WithPhotoTemplate(this, user, displayIndex); } /** * select the user in the model and in the view * @param displayIndex */ public void select(int displayIndex) { for (int i = 0; i < mainPanel.getWidgetCount(); i++) { Widget ut = (Widget) mainPanel.getWidget(i); if (i == displayIndex) { ut.addStyleName("pickperson-selected"); displayIndexSelected = i; } else ut.removeStyleName("pickperson-selected"); } } }