com.mecatran.otp.gwt.client.view.AddressProposalBox.java Source code

Java tutorial

Introduction

Here is the source code for com.mecatran.otp.gwt.client.view.AddressProposalBox.java

Source

/* -------------------------------------------------------------------------
OpenTripPlanner GWT Client
Copyright (C) 2015 Mecatran - info@mecatran.com
    
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   ------------------------------------------------------------------------- */
package com.mecatran.otp.gwt.client.view;

import java.util.List;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
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.user.client.Timer;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.mecatran.otp.gwt.client.i18n.I18nUtils;
import com.mecatran.otp.gwt.client.model.LocationBean;
import com.mecatran.otp.gwt.client.proxies.GeocoderProxy;
import com.mecatran.otp.gwt.client.proxies.GeocoderProxy.GeocoderListener;

/**
 * Implementation of an AddressBox which call the geocoder only on component
 * blur and/or input timeout. If multiple results are found ask for the best
 * one.
 * 
 */
public class AddressProposalBox extends Composite implements AddressBox {

    private TextBox textBox;
    private Button clearButton;
    private VerticalPanel proposalPanel;
    private DialogBox proposalDialog;
    private LocationBean location;
    private AddressSuggestBoxListener listener;
    private GeocoderProxy geocoderProxy;
    private int geocodeRecursiveMutex = 0;
    private Timer geocodeTimer;
    private boolean modifiedSinceGeocoding = true;

    public AddressProposalBox() {
        geocodeTimer = new Timer() {
            @Override
            public void run() {
                if (!modifiedSinceGeocoding)
                    return;
                modifiedSinceGeocoding = false;
                if (!textBox.getText().isEmpty() && location.getLocation() == null) {
                    geocode();
                }
            }
        };

        location = new LocationBean();
        VerticalPanel rootPanel = new VerticalPanel();
        HorizontalPanel inputPanel = new HorizontalPanel();
        rootPanel.add(inputPanel);
        inputPanel.setCellWidth(inputPanel, "100%");
        textBox = new TextBox();
        textBox.addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                location.setAddress(textBox.getText());
                location.setLocation(null);
                if (textBox.getText().isEmpty())
                    listener.onLocationSelected(location);
            }
        });
        textBox.addKeyPressHandler(new KeyPressHandler() {
            @Override
            public void onKeyPress(KeyPressEvent event) {
                modifiedSinceGeocoding = true;
                location.setLocation(null);
                geocodeTimer.cancel();
                if (textBox.getText().length() > 10) {
                    geocodeTimer.schedule(3000);
                }
            }
        });
        textBox.addFocusHandler(new FocusHandler() {
            @Override
            public void onFocus(FocusEvent event) {
                listener.onFocus();
            }
        });
        textBox.addBlurHandler(new BlurHandler() {
            @Override
            public void onBlur(BlurEvent event) {
                geocodeTimer.cancel();
                geocodeTimer.schedule(100);
            }
        });
        inputPanel.add(textBox);
        clearButton = new Button("X");
        inputPanel.setCellWidth(inputPanel, "auto");
        clearButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                geocodeTimer.cancel();
                textBox.setText("");
                location.setAddress("");
                location.setLocation(null);
                listener.onLocationSelected(location);
            }
        });
        inputPanel.add(clearButton);
        proposalPanel = new VerticalPanel();
        initWidget(rootPanel);
    }

    @Override
    public void setAddressSuggestBoxListener(AddressSuggestBoxListener listener) {
        this.listener = listener;
    }

    @Override
    public LocationBean getLocation() {
        location.setAddress(textBox.getText());
        return location;
    }

    @Override
    public void setLocation(LocationBean location) {
        this.location = location;
        if (location.getAddress() != null && !location.getAddress().isEmpty()) {
            textBox.setText(location.getAddress());
            if (location.getLocation() == null && geocodeRecursiveMutex == 0) {
                geocode();
            }
        }
    }

    @Override
    public void setGeocoderProxy(GeocoderProxy geocoderProxy) {
        this.geocoderProxy = geocoderProxy;
    }

    @Override
    public Widget getAsWidget() {
        return this;
    }

    private void geocode() {
        if (geocoderProxy == null)
            return;
        geocoderProxy.geocode(textBox.getText(), new GeocoderListener() {

            @Override
            public void onGeocodingDone(List<LocationBean> locations) {
                proposalPanel.clear();
                if (locations == null || locations.size() == 0) {
                    // No answer
                    // TODO Set special style name and message
                    // Prevent from recursively geocoding for a null location
                    geocodeRecursiveMutex++;
                    location.setLocation(null);
                    listener.onLocationSelected(location);
                    geocodeRecursiveMutex--;
                    Label noAddressFound = new Label(I18nUtils.tr("address.not.found"));
                    noAddressFound.addStyleName("warn-icon");
                    noAddressFound.addStyleName("no-address-found");
                    proposalPanel.add(noAddressFound);
                    showProposals();
                } else if (locations.size() == 1) {
                    // One answer: take this
                    location = locations.get(0);
                    textBox.setText(location.getAddress());
                    listener.onLocationSelected(location);
                } else {
                    // Several answers: make a proposal
                    // And wait for the user to select one.
                    int n = 0;
                    Label severalAddressFound = new Label(I18nUtils.tr("several.address.matches"));
                    severalAddressFound.addStyleName("several-address-found");
                    proposalPanel.add(severalAddressFound);
                    for (LocationBean location : locations) {
                        if (n >= 10) // TODO Make this parametrable
                            break;
                        GeocoderAddressProposal gap = new GeocoderAddressProposal(location);
                        gap.setStyleName("geocoder-address-proposal");
                        proposalPanel.add(gap);
                        n++;
                    }
                    showProposals();
                }
            }
        });
    }

    private void showProposals() {
        proposalDialog = new DialogBox();
        proposalDialog.setAnimationEnabled(true);
        VerticalPanel rootPanel = new VerticalPanel();
        proposalDialog.add(rootPanel);
        rootPanel.add(proposalPanel);
        HorizontalPanel buttonPanel = new HorizontalPanel();
        rootPanel.add(buttonPanel);
        Button cancelButton = new Button(I18nUtils.tr("cancel"));
        buttonPanel.add(cancelButton);
        cancelButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                hideProposals();
            }
        });
        proposalDialog.setAutoHideEnabled(true);
        proposalDialog.showRelativeTo(getAsWidget());
    }

    private void hideProposals() {
        if (proposalDialog != null) {
            proposalDialog.hide();
            proposalDialog = null;
        }
        proposalPanel.clear();
    }

    private void proposalSelected(LocationBean location) {
        this.location = location;
        textBox.setText(location.getAddress());
        listener.onLocationSelected(this.location);
        hideProposals();
    }

    private class GeocoderAddressProposal extends Composite {

        private LocationBean location;

        public GeocoderAddressProposal(LocationBean aLocation) {
            this.location = aLocation;
            SimplePanel rootPanel = new SimplePanel();
            Anchor link = new Anchor(location.getAddress());
            link.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    listener.onLocationHover(null);
                    proposalSelected(GeocoderAddressProposal.this.location);
                }
            });
            link.addMouseOverHandler(new MouseOverHandler() {
                @Override
                public void onMouseOver(MouseOverEvent event) {
                    listener.onLocationHover(location);
                }
            });
            link.addMouseOutHandler(new MouseOutHandler() {
                @Override
                public void onMouseOut(MouseOutEvent event) {
                    listener.onLocationHover(null);
                }
            });
            rootPanel.add(link);
            initWidget(rootPanel);
        }
    }

}