org.unitime.timetable.gwt.client.aria.AriaSuggestBox.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.timetable.gwt.client.aria.AriaSuggestBox.java

Source

/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 *
 * The Apereo Foundation licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
*/
package org.unitime.timetable.gwt.client.aria;

import java.util.List;

import org.unitime.timetable.gwt.client.ToolBox;
import org.unitime.timetable.gwt.resources.GwtAriaMessages;

import com.google.gwt.aria.client.AutocompleteValue;
import com.google.gwt.aria.client.Id;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HandlesAllKeyEvents;
import com.google.gwt.event.dom.client.HasBlurHandlers;
import com.google.gwt.event.dom.client.HasFocusHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.ValueBoxBase;
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;

/**
 * @author Tomas Muller
 */
public class AriaSuggestBox extends Composite implements HasText, HasValue<String>,
        HasSelectionHandlers<Suggestion>, Focusable, HasEnabled, HasAriaLabel {
    private static GwtAriaMessages ARIA = GWT.create(GwtAriaMessages.class);
    private AriaTextBox iText;
    private SuggestOracle iOracle;

    private PopupPanel iSuggestionPopup;
    private SuggestionMenu iSuggestionMenu;
    private ScrollPanel iPopupScroll;
    private SuggestionCallback iSuggestionCallback;
    private SuggestOracle.Callback iOracleCallback;

    private String iCurrentText = null;

    public AriaSuggestBox(SuggestOracle oracle) {
        this(new AriaTextBox(), oracle);
    }

    public AriaSuggestBox(AriaTextBox box, SuggestOracle oracle) {
        iOracle = oracle;
        iText = box;
        iText.setStyleName("gwt-SuggestBox");
        initWidget(iText);

        addEventsToTextBox();

        iSuggestionMenu = new SuggestionMenu();

        iPopupScroll = new ScrollPanel(iSuggestionMenu);
        iPopupScroll.addStyleName("scroll");

        iSuggestionPopup = new PopupPanel(true, false);
        iSuggestionPopup.setPreviewingAllNativeEvents(true);
        iSuggestionPopup.setStyleName("unitime-SuggestBoxPopup");
        iSuggestionPopup.setWidget(iPopupScroll);
        iSuggestionPopup.addAutoHidePartner(getElement());

        iSuggestionCallback = new SuggestionCallback() {
            @Override
            public void onSuggestionSelected(Suggestion suggestion) {
                if (!suggestion.getReplacementString().isEmpty()) {
                    setStatus(ARIA.suggestionSelected(status(suggestion)));
                }
                iCurrentText = suggestion.getReplacementString();
                setText(suggestion.getReplacementString());
                hideSuggestionList();
                fireSuggestionEvent(suggestion);
            }
        };

        iOracleCallback = new SuggestOracle.Callback() {
            @Override
            public void onSuggestionsReady(Request request, Response response) {
                if (response.getSuggestions() == null || response.getSuggestions().isEmpty()) {
                    if (iSuggestionPopup.isShowing())
                        iSuggestionPopup.hide();
                } else {
                    iSuggestionMenu.clearItems();
                    SuggestOracle.Suggestion first = null;
                    for (SuggestOracle.Suggestion suggestion : response.getSuggestions()) {
                        iSuggestionMenu.addItem(new SuggestionMenuItem(suggestion));
                        if (first == null)
                            first = suggestion;
                    }
                    iSuggestionMenu.selectItem(0);
                    ToolBox.setMinWidth(iSuggestionMenu.getElement().getStyle(),
                            (iText.getElement().getClientWidth() - 4) + "px");
                    iSuggestionPopup.showRelativeTo(iText);
                    iSuggestionMenu.scrollToView();
                    if (response.getSuggestions().size() == 1) {
                        if (first.getReplacementString().isEmpty())
                            setStatus(status(first));
                        else
                            setStatus(ARIA.showingOneSuggestion(status(first)));
                    } else {
                        setStatus(ARIA.showingMultipleSuggestions(response.getSuggestions().size(),
                                request.getQuery(), status(first)));
                    }
                }
            }
        };

        Roles.getTextboxRole().setAriaAutocompleteProperty(iText.getElement(), AutocompleteValue.NONE);

        iSuggestionPopup.getElement().setAttribute("id", DOM.createUniqueId());
        Roles.getTextboxRole().setAriaOwnsProperty(iText.getElement(), Id.of(iSuggestionPopup.getElement()));
    }

    private String status(Suggestion suggestion) {
        return suggestion instanceof HasStatus ? ((HasStatus) suggestion).getStatusString()
                : suggestion.getDisplayString();
    }

    public void setStatus(String text) {
        AriaStatus.getInstance().setText(text);
    }

    private void addEventsToTextBox() {
        class TextBoxEvents extends HandlesAllKeyEvents implements ValueChangeHandler<String> {
            public void onKeyDown(KeyDownEvent event) {
                switch (event.getNativeKeyCode()) {
                case KeyCodes.KEY_DOWN:
                    if (moveSelectionDown()) {
                        event.preventDefault();
                        return;
                    }
                    if (!isSuggestionListShowing() && (event.getNativeEvent().getAltKey()
                            || iText.getCursorPos() == iText.getText().length()))
                        showSuggestionList();
                    break;
                case KeyCodes.KEY_UP:
                    if (moveSelectionUp()) {
                        event.preventDefault();
                        return;
                    }
                    break;
                case KeyCodes.KEY_ENTER:
                    if (isSuggestionListShowing())
                        iSuggestionMenu.executeSelected();
                    break;
                case KeyCodes.KEY_TAB:
                    if (isSuggestionListShowing()) {
                        if (getText().isEmpty())
                            hideSuggestionList();
                        else
                            iSuggestionMenu.executeSelected();
                    }
                    break;
                case KeyCodes.KEY_ESCAPE:
                    if (isSuggestionListShowing())
                        hideSuggestionList();
                }
                delegateEvent(AriaSuggestBox.this, event);
            }

            public void onKeyPress(KeyPressEvent event) {
                delegateEvent(AriaSuggestBox.this, event);
            }

            public void onKeyUp(KeyUpEvent event) {
                refreshSuggestions();
                delegateEvent(AriaSuggestBox.this, event);
            }

            public void onValueChange(ValueChangeEvent<String> event) {
                delegateEvent(AriaSuggestBox.this, event);
            }
        }

        TextBoxEvents events = new TextBoxEvents();
        events.addKeyHandlersTo(iText);
        iText.addValueChangeHandler(events);
    }

    private boolean moveSelectionDown() {
        if (!isSuggestionListShowing())
            return false;
        if (iSuggestionMenu.selectItem(iSuggestionMenu.getSelectedItemIndex() + 1)) {
            if (iSuggestionMenu.getNumItems() > 1)
                setStatus(ARIA.onSuggestion(iSuggestionMenu.getSelectedItemIndex() + 1,
                        iSuggestionMenu.getNumItems(), status(iSuggestionMenu.getSelectedSuggestion())));
            return true;
        } else {
            return false;
        }
    }

    private boolean moveSelectionUp() {
        if (!isSuggestionListShowing())
            return false;
        boolean selected = false;
        if (iSuggestionMenu.getSelectedItemIndex() == -1) {
            selected = iSuggestionMenu.selectItem(iSuggestionMenu.getNumItems() - 1);
        } else {
            selected = iSuggestionMenu.selectItem(iSuggestionMenu.getSelectedItemIndex() - 1);
        }
        if (selected) {
            if (iSuggestionMenu.getNumItems() > 1)
                setStatus(ARIA.onSuggestion(iSuggestionMenu.getSelectedItemIndex() + 1,
                        iSuggestionMenu.getNumItems(), status(iSuggestionMenu.getSelectedSuggestion())));
            return true;
        } else {
            return false;
        }
    }

    public void showSuggestionList() {
        iCurrentText = null;
        refreshSuggestions();
    }

    private void refreshSuggestions() {
        String text = getText();
        if (text.equals(iCurrentText)) {
            return;
        } else {
            iCurrentText = text;
        }
        showSuggestions(text);
    }

    public void hideSuggestionList() {
        if (iSuggestionPopup.isShowing())
            iSuggestionPopup.hide();
    }

    public void showSuggestions(String text) {
        if (text.isEmpty())
            iOracle.requestDefaultSuggestions(new Request(null), iOracleCallback);
        else
            iOracle.requestSuggestions(new Request(text), iOracleCallback);
    }

    public boolean isSuggestionListShowing() {
        return iSuggestionPopup.isShowing();
    }

    public SuggestionMenu getSuggestionMenu() {
        return iSuggestionMenu;
    }

    private class SuggestionMenu extends MenuBar implements HasFocusHandlers, HasBlurHandlers {
        SuggestionMenu() {
            super(true);
            setStyleName("");
            setFocusOnHoverEnabled(false);
            sinkEvents(Event.ONBLUR);
            sinkEvents(Event.ONFOCUS);
        }

        @Override
        public void onBrowserEvent(Event event) {
            switch (DOM.eventGetType(event)) {
            case Event.ONBLUR:
                BlurEvent.fireNativeEvent(event, this);
                break;
            case Event.ONFOCUS:
                FocusEvent.fireNativeEvent(event, this);
                break;
            }
            super.onBrowserEvent(event);
        }

        public int getNumItems() {
            return getItems().size();
        }

        public int getSelectedItemIndex() {
            MenuItem selectedItem = getSelectedItem();
            if (selectedItem != null)
                return getItems().indexOf(selectedItem);
            return -1;
        }

        public boolean selectItem(int index) {
            List<MenuItem> items = getItems();
            if (index > -1 && index < items.size()) {
                selectItem(items.get(index));
                iPopupScroll.ensureVisible(items.get(index));
                return true;
            }
            return false;
        }

        public void scrollToView() {
            List<MenuItem> items = getItems();
            int index = getSelectedItemIndex();
            if (index > -1 && index < items.size()) {
                iPopupScroll.ensureVisible(items.get(index));
            }
        }

        public boolean executeSelected() {
            MenuItem selected = getSelectedItem();
            if (selected == null)
                return false;
            selected.getScheduledCommand().execute();
            return true;
        }

        public Suggestion getSelectedSuggestion() {
            MenuItem selectedItem = getSelectedItem();
            return selectedItem == null ? null : ((SuggestionMenuItem) selectedItem).getSuggestion();
        }

        @Override
        public HandlerRegistration addBlurHandler(BlurHandler handler) {
            return addHandler(handler, BlurEvent.getType());
        }

        @Override
        public HandlerRegistration addFocusHandler(FocusHandler handler) {
            return addHandler(handler, FocusEvent.getType());
        }
    }

    private class SuggestionMenuItem extends MenuItem {
        private Suggestion iSuggestion = null;

        private SuggestionMenuItem(final Suggestion suggestion) {
            super(suggestion.getDisplayString(), iOracle.isDisplayStringHTML(), new ScheduledCommand() {
                @Override
                public void execute() {
                    iSuggestionCallback.onSuggestionSelected(suggestion);
                }
            });
            setStyleName("item");
            getElement().setAttribute("whiteSpace", "nowrap");
            iSuggestion = suggestion;
        }

        public Suggestion getSuggestion() {
            return iSuggestion;
        }
    }

    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }

    @Override
    public String getValue() {
        return iText.getValue();
    }

    @Override
    public void setValue(String value) {
        setValue(value, false);
    }

    @Override
    public void setValue(String value, boolean fireEvents) {
        iText.setValue(value);
        if (fireEvents)
            ValueChangeEvent.fire(this, getValue());
    }

    @Override
    public String getText() {
        return iText.getText();
    }

    @Override
    public void setText(String text) {
        iText.setText(text);
    }

    @Override
    public HandlerRegistration addSelectionHandler(SelectionHandler<Suggestion> handler) {
        return addHandler(handler, SelectionEvent.getType());
    }

    private void fireSuggestionEvent(Suggestion selectedSuggestion) {
        SelectionEvent.fire(this, selectedSuggestion);
    }

    @Override
    public int getTabIndex() {
        return iText.getTabIndex();
    }

    @Override
    public void setAccessKey(char key) {
        iText.setAccessKey(key);
    }

    @Override
    public void setFocus(boolean focused) {
        iText.setFocus(focused);
    }

    @Override
    public void setTabIndex(int index) {
        iText.setTabIndex(index);
    }

    @Override
    public boolean isEnabled() {
        return iText.isEnabled();
    }

    @Override
    public void setEnabled(boolean enabled) {
        iText.setEnabled(enabled);
    }

    public ValueBoxBase<String> getValueBox() {
        return iText;
    }

    public static interface SuggestionCallback {
        void onSuggestionSelected(Suggestion suggestion);
    }

    public static interface HasStatus {
        public String getStatusString();
    }

    @Override
    public String getAriaLabel() {
        return iText.getAriaLabel();
    }

    @Override
    public void setAriaLabel(String text) {
        iText.setAriaLabel(text);
    }
}