Subversion Repositories eFlore/Applications.cel

Rev

Rev 2640 | Rev 2734 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package org.tela_botanica.client.util.autocompletion;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.tela_botanica.client.cel2;
import org.tela_botanica.client.modeles.objets.Utilisateur;

import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
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.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.SuggestBox.SuggestionDisplay;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.gwtext.client.widgets.BoxComponent;
import com.gwtext.client.widgets.Component;
import com.gwtext.client.widgets.Container;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.event.PanelListener;
import com.gwtext.client.widgets.event.PanelListenerAdapter;
import com.gwtext.client.widgets.layout.FitLayout;

@SuppressWarnings("unchecked")
public abstract class AutoCompletionComboBox extends Panel {
                
        Label label = null;
        MultiWordSuggestOracle msu = null;
        SuggestBox suggestions = null;
        DefaultSuggestionDisplay sgd = null;
        
        Callback<String, String> surCopierColler = null;
        
        SuggestOracle.Request requeteOracle;
    SuggestOracle.Callback callBackOracle;
    
        Timer suggestionTimer = null;
    int tempsAvantRequete = 500;
    
    Map<String, String> possibilites = null;
    
    String urlRequete = "";
        
        public AutoCompletionComboBox(String nomLabel, String urlRequete) {
                
                this.setStyleName("champAutoComplete");
                
                this.label = new Label(nomLabel+":");
                this.urlRequete = urlRequete;
                
                sgd = new SuggestBox.DefaultSuggestionDisplay();
                msu = new MultiWordSuggestOracle() {
                        @Override
                        public void requestSuggestions(Request request, Callback callback) {
                                List<MultiWordSuggestion> retenus = new ArrayList<MultiWordSuggestion>();
                                Iterator<String> it = possibilites.keySet().iterator();
                            while (it.hasNext()) {
                                String cle = it.next();
                                String cleAffichee = preTraiterValeurAvantAffichage(possibilites.get(cle));
                                        MultiWordSuggestion muWoSug = new MultiWordSuggestion(possibilites.get(cle), cleAffichee);
                                        retenus.add(muWoSug);
                            }

                                Response reponse = new Response(retenus);
                                callback.onSuggestionsReady(request, reponse);
                        }               
                };

                suggestions = new SuggestBox(msu, new TextBox(), sgd) {
                        public void onBrowserEvent(Event event) {
                            switch (event.getTypeInt()) {
                                case Event.ONPASTE: {
                                        if(surCopierColler != null) {
                                                surCopierColler.onSuccess(suggestions.getValueBox().getValue());
                                        }
                                }
                                break;
                                default:
                                        // WTF: Curieux bloc qui est nécessaire car si on appelle pas super.onBrowserEvent
                                        // aucun évènement ne fonctionne et si on le fait, cela génère des nullPointerException
                                        // donc on transige en l'appelant dans un try catch
                                        try {
                                                super.onBrowserEvent(event);
                                        } catch (Exception e) {
                                                // à priori rien à faire
                                        }
                            }
                            
                        }
                };
                
                suggestions.setAutoSelectEnabled(false);
                setLayout(new FitLayout());
                add(label);
                add(suggestions);
                gererEvenements();
        }
        
        public void gererEvenements() {
                suggestions.addKeyDownHandler(new KeyDownHandler() {

                    @Override
                    public void onKeyDown(KeyDownEvent event) {         
                        // Lorsque la liste de suggestions est ouverte, une frappe sur entrée selectionne la valeur
                        // Si elle est fermée, elle déclenche la fonction abstraite de validation (utile pour les champs
                        // du genre station, milieu etc... et la saisie d'obs rapide
                                if(event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {    
                                        if((sgd == null || !sgd.isSuggestionListShowing())) {
                                                onValidationSaisie();
                                                event.stopPropagation();
                                                return;
                                        } else {
                                                // Dans les autres cas, un appui sur entrée ne doit pas redéclencher l'autcomplétion
                                                return;
                                        }
                                }
                                
                                if(event.getNativeKeyCode() == KeyCodes.KEY_DOWN) {
                                        // Un appui sur flèche bas permet de demander toute la liste
                                        if(!sgd.isSuggestionListShowing()) {
                                                requeteToutesSuggestions();
                                        }
                                }
                                
                                // Tout autre frappe d'un caractère "normal" déclenche le timer d'autocomplétion
                                if(!KeyDownEvent.isArrow(event.getNativeKeyCode())) {
                                        gererTimer();
                                }
                    }

                });
                
                requeteOracle = new SuggestOracle.Request();
                callBackOracle = new SuggestOracle.Callback() {

                        public void onSuggestionsReady(SuggestOracle.Request request, SuggestOracle.Response response) {
                                suggestions.showSuggestionList();
                        }
            };
            
            suggestions.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>(){                           
                        @Override
                        public void onSelection(SelectionEvent<Suggestion> event) {
                                onSelectionValeur();
                        }
                });
            
            suggestions.addValueChangeHandler(new ValueChangeHandler<String>() {
                        @Override
                        public void onValueChange(ValueChangeEvent<String> event) {
                                onModificationValeur();
                        }
                });
            
            surCopierColler = new Callback<String, String>() {          
                        @Override
                        public void onSuccess(String result) {
                                gererTimer();
                        }
                        
                        @Override
                        public void onFailure(String reason) {
                                // rien à faire ça n'échoue jamais
                        }
                };
                
                addListener(new PanelListenerAdapter() {
                        
                        @Override
                        public void onResize(BoxComponent component, int adjWidth, int adjHeight,
                                        int rawWidth, int rawHeight) {
                                redimensionner(rawWidth);
                        }
                });
        }
        
        public void redimensionner(final int largeur) {
                suggestions.setWidth((largeur - label.getOffsetWidth() - 13)+"px");
        }
        
        public void gererTimer() {
                if(suggestionTimer == null) {
                        suggestionTimer = new Timer() {
                                public void run() {
                                        requeteSuggestions();
                                }       
                        };                                      
                        suggestionTimer.schedule(tempsAvantRequete);
                } else {
                        suggestionTimer.schedule(tempsAvantRequete);
                }
        }
        
        public void setTempsAvantRequete(int temps) {
                tempsAvantRequete = temps;
        }
        
        public int getTempsAvantRequete(int temps) {
                return tempsAvantRequete;
        }
        
        public void requeteToutesSuggestions() {
                // Cette méthode permet de demander l'autocomplétion même si 
                // la valeur saisie est vide (cas de l'appui sur flèche bas)
                envoyerRequeteSuggestions("*");
        }
        
        public void requeteSuggestions() {
                // Suppression des espaces et caractères inutiles
                String valeur = suggestions.getValue().trim().isEmpty() ? null : suggestions.getValue().trim(); 
                
                // ceci permet au composant qui utilise l'autocomplétion
                // d'annuler une requete et également d'éviter de faire une requête lors du vidage du champ
                if(valeur == null) {
                        return;
                }
                envoyerRequeteSuggestions(valeur);
        }
        
        private void envoyerRequeteSuggestions(final String valeur) {
                final String urlRequeteFmt = preTraiterUrlRequete(urlRequete, valeur);  
                RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, urlRequeteFmt) ;
                try {           
                        rb.sendRequest(null, new RequestCallback() {
                                public void onError(final Request request, final Throwable exception) {
                                        // TODO Auto-generated method stub              
                                }

                                public void onResponseReceived(final Request request,
                                                final Response response) {
                                        surReponseRequete(valeur, response);
                                }
                        }) ;

                } catch (RequestException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
        }
        
        public void surReponseRequete(String requete, Response response) {
                possibilites = parserResultatRequete(response);
                msu.clear();
                Iterator<String> it = possibilites.keySet().iterator();
            while (it.hasNext()) {
                msu.add(possibilites.get(it.next()));
            }
                        
                requeteOracle.setQuery(requete);
                if(requete.equals("*")) {
                        // TODO : il y a peut être une méthode plus simple
                        // mais ceci permet d'afficher les suggestions en cas d'appui
                        // sur bas lorsque la liste est vide
                        msu.setDefaultSuggestionsFromText(possibilites.values());       
                        SuggestOracle.Request rq = new SuggestOracle.Request();
                        rq.setQuery(null);
                        msu.requestDefaultSuggestions(rq, callBackOracle);
                } else {
                        msu.requestSuggestions(requeteOracle, callBackOracle);  
                }
        }
        
        public String getText() {
                return suggestions.getText();
        }
        
        public String getRawValue() {
                return suggestions.getText();
        }
        
        public void setValue(String texte) {
                suggestions.setText(texte);
        }
        
        public void reset() {
                suggestions.setValue("");
        }
        
        public void setTabIndex(int index) {
                suggestions.setTabIndex(index);
        }
        
        public String getLabelText() {
                return label.getText();
        }
        
        public int getFieldWidth() {
                return suggestions.getOffsetWidth();
        }
        
        public int getLabelWidth() {
                return label.getOffsetWidth();
        }
        
        public void agrandirChamp(int largeur) {
                suggestions.setWidth(largeur+"px");
        }
        
        protected String preTraiterValeurAvantAffichage(String valeur) {
                return valeur;
        }
        protected String preTraiterUrlRequete(String urlRequete, String valeur) {
                return urlRequete+URL.encodeQueryString(valeur);
        }
        protected abstract Map<String, String> parserResultatRequete(Response response);        
        protected abstract void onModificationValeur();
        protected abstract void onSelectionValeur();
        protected abstract void onValidationSaisie();
}