OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.sql.sqlobject;

import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.MultipleSQLSelectExecutor;
import org.openconcerto.ui.component.text.TextComponent;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.OrderedSet;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.checks.MutableValueObject;
import org.openconcerto.utils.model.DefaultIMutableListModel;
import org.openconcerto.utils.text.DocumentFilterList;
import org.openconcerto.utils.text.DocumentFilterList.FilterType;
import org.openconcerto.utils.text.LimitedSizeDocumentFilter;

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.DocumentFilter;
import javax.swing.text.JTextComponent;

public class ITextArticleWithCompletion extends JPanel implements DocumentListener, TextComponent, MutableValueObject<String>, IComboSelectionItemListener {

    public static int SQL_RESULT_LIMIT = 50;

    public static final int MODE_STARTWITH = 1;
    public static final int MODE_CONTAINS = 2;

    private JTextComponent text;

    private DefaultIMutableListModel<IComboSelectionItem> model = new DefaultIMutableListModel<IComboSelectionItem>();

    private boolean completionEnabled = true;

    private SQLRowAccessor selectedRow = null;

    private boolean selectAuto = true;

    protected ITextWithCompletionPopUp popup;

    OrderedSet<SelectionRowListener> listeners = new OrderedSet<SelectionRowListener>();
    Component popupInvoker;

    private boolean isLoading = false;
    private SQLRowAccessor rowToSelect = null;

    private String fillWith = "CODE";
    private final PropertyChangeSupport supp;

    private final SQLTable tableArticle, tableArticleFournisseur;

    // Asynchronous filling
    private Thread searchThread;
    private int autoCheckDelay = 1000;
    private boolean disposed = false;
    private Stack<String> searchStack = new Stack<String>();
    private boolean autoselectIfMatch;
    private static final int PAUSE_MS = 150;
    private final boolean hasDeclinaison;

    public ITextArticleWithCompletion(SQLTable tableArticle, SQLTable tableARticleFournisseur, boolean withDeclinaison) {
        this.tableArticle = tableArticle;
        this.tableArticleFournisseur = tableARticleFournisseur;
        this.hasDeclinaison = withDeclinaison;
        this.supp = new PropertyChangeSupport(this);
        this.popup = new ITextWithCompletionPopUp(this.model, this);
        this.popup.setMinWith(450);
        this.text = new JTextField();
        this.setLayout(new GridLayout(1, 1));
        this.add(this.text);
        setTextEditor(this.text);
        setPopupInvoker(this);

        //
        disposed = false;
        searchThread = new Thread() {
            public void run() {
                while (!disposed) {
                    if (autoCheckDelay == 0) {
                        autoCheckDelay = -1;
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                loadAutoCompletion();
                            }
                        });

                    } else if (autoCheckDelay > 0) {
                        autoCheckDelay -= PAUSE_MS;
                    }
                    try {
                        Thread.sleep(PAUSE_MS);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            };
        };
        searchThread.setName("ITextArticleWithCompletion thread");
        searchThread.setPriority(Thread.MIN_PRIORITY);
        searchThread.setDaemon(true);
        searchThread.start();

    }

    public void setPopupListEnabled(boolean b) {
        this.popup.setListEnabled(b);
    }

    private Where whereAdditionnal;
    private ITransformer<SQLSelect, SQLSelect> selTransformer;

    public void setWhere(Where w) {
        this.whereAdditionnal = w;
    }

    public void setSelectTransformer(ITransformer<SQLSelect, SQLSelect> selTransformer) {
        this.selTransformer = selTransformer;
    }

    public void setTextEditor(final JTextComponent atext) {
        if (atext == null) {
            throw new IllegalArgumentException("null textEditor");
        }
        this.text = atext;
        atext.getDocument().addDocumentListener(this);
        atext.addKeyListener(new KeyListener() {

            private boolean consume;

            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_TAB) {
                    // Complete si exactement la valeur souhaitée
                    updateAutoCompletion(true);
                    e.consume();

                } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
                        ITextArticleWithCompletion.this.popup.selectNext();
                        e.consume();
                    } else {
                        if (getSelectedRow() == null) {
                            // updateAutoCompletion();
                            showPopup();
                        }
                    }

                } else if (e.getKeyCode() == KeyEvent.VK_UP) {
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
                        ITextArticleWithCompletion.this.popup.selectPrevious();
                        e.consume();
                    }

                } else if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB) {
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
                        ITextArticleWithCompletion.this.popup.validateSelection();
                        e.consume();
                    } else {
                        autoselectIfMatch = true;
                        e.consume();
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
                        ITextArticleWithCompletion.this.popup.selectNextPage();
                        e.consume();
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_PAGE_UP) {
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
                        ITextArticleWithCompletion.this.popup.selectPreviousPage();
                        e.consume();
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
                        hidePopup();
                    }

                }

                // else {
                // if (e.getKeyCode() != KeyEvent.VK_RIGHT && e.getKeyCode() !=
                // KeyEvent.VK_LEFT) {
                // fireSelectionId(-1);
                // }
                // }
                // Evite les bips
                if (ITextArticleWithCompletion.this.text.getDocument().getLength() == 0 && (e.getKeyCode() == KeyEvent.VK_DELETE || e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) {
                    System.err.println("consume");
                    this.consume = true;
                    e.consume();
                }

            }

            public void keyReleased(KeyEvent e) {
            }

            public void keyTyped(KeyEvent e) {
                // Evite les bips
                if (this.consume) {
                    e.consume();
                    this.consume = false;
                }
            }
        });
        this.addComponentListener(new ComponentListener() {
            public void componentHidden(ComponentEvent e) {
            }

            public void componentMoved(ComponentEvent e) {
            }

            public void componentResized(ComponentEvent e) {
                // ajuste la taille min de la popup
                ITextArticleWithCompletion.this.popup.setMinWith(atext.getBounds().width);
            }

            public void componentShown(ComponentEvent e) {
            }
        });
        atext.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {

            }

            @Override
            public void focusLost(FocusEvent e) {
                hidePopup();
            }
        });
    }

    /**
     * Retourne une liste de IComboSelectionItem, qui sont les selections possibles pour le text
     * passé
     * 
     * @throws SQLException
     */
    List<IComboSelectionItem> getPossibleValues(String aText) throws SQLException {
        List<IComboSelectionItem> result = new Vector<IComboSelectionItem>();
        if (aText.isEmpty()) {
            return result;
        }
        aText = aText.trim();

        if (aText.length() > 0) {

            List<SQLSelect> listSel = new ArrayList<SQLSelect>();

            // CODE ARTICLE = aText
            SQLSelect selMatchingCode = new SQLSelect();

            selMatchingCode.addSelect(this.tableArticle.getKey());
            selMatchingCode.addSelect(this.tableArticle.getField("CODE"));
            selMatchingCode.addSelect(this.tableArticle.getField("NOM"));
            selMatchingCode.addSelect(this.tableArticle.getField("CODE_BARRE"));
            if (this.hasDeclinaison) {
                for (String fieldName : this.tableArticle.getFieldsName()) {
                    if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
                        selMatchingCode.addSelect(this.tableArticle.getField(fieldName));

                        SQLSelect selDecl = new SQLSelect();
                        SQLTable tableDecl = this.tableArticle.getForeignTable(fieldName);
                        selDecl.addSelect(tableDecl.getKey());
                        selDecl.addSelect(tableDecl.getField("NOM"));
                        listSel.add(selDecl);
                    }
                }
            }
            Where wMatchingCode = new Where(this.tableArticle.getField("CODE"), "=", aText);
            wMatchingCode = wMatchingCode.or(new Where(this.tableArticle.getField("NOM"), "=", aText));
            wMatchingCode = wMatchingCode.or(new Where(this.tableArticle.getField("CODE_BARRE"), "=", aText));
            wMatchingCode = wMatchingCode.and(new Where(this.tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));
            if (this.whereAdditionnal != null) {
                wMatchingCode = wMatchingCode.and(this.whereAdditionnal);
            }
            selMatchingCode.setWhere(wMatchingCode);
            if (this.selTransformer != null) {
                selMatchingCode = this.selTransformer.transformChecked(selMatchingCode);
            }
            listSel.add(selMatchingCode);

            // CODE ARTICLE LIKE %aText% with limit
            SQLSelect selContains = new SQLSelect();
            // selContains.addSelectStar(this.tableArticle);
            selContains.addSelect(this.tableArticle.getKey());
            selContains.addSelect(this.tableArticle.getField("CODE"));
            selContains.addSelect(this.tableArticle.getField("NOM"));
            selContains.addSelect(this.tableArticle.getField("CODE_BARRE"));
            if (this.hasDeclinaison) {
                for (String fieldName : this.tableArticle.getFieldsName()) {
                    if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
                        selContains.addSelect(this.tableArticle.getField(fieldName));
                    }
                }
            }
            Where wContains = new Where(this.tableArticle.getField("CODE"), "LIKE", "%" + aText + "%");            
            wContains = wContains.or(new Where(this.tableArticle.getField("NOM"), "LIKE", "%" + aText + "%"));
            wContains = wContains.or(new Where(this.tableArticle.getField("CODE_BARRE"), "LIKE", "%" + aText + "%"));
            wContains = wContains.and(new Where(this.tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));
            if (this.whereAdditionnal != null) {
                wContains = wContains.and(this.whereAdditionnal);
            }

            selContains.setWhere(wContains.and(wMatchingCode.not()));
            selContains.setExcludeUndefined(false, this.tableArticle.getForeignTable("ID_STOCK"));
            selContains.setLimit(SQL_RESULT_LIMIT);
            if (this.selTransformer != null) {
                selContains = this.selTransformer.transformChecked(selContains);
            }
            listSel.add(selContains);

            // CODE ARTICLE = aText
            final Where wNotSync = new Where(this.tableArticleFournisseur.getField("ID_ARTICLE"), "IS", (Object) null)
                    .or(new Where(this.tableArticleFournisseur.getField("ID_ARTICLE"), "=", this.tableArticleFournisseur.getUndefinedID()));

            SQLSelect selMatchingCodeF = new SQLSelect();
            // selMatchingCodeF.addSelectStar(this.tableArticleFournisseur);
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getKey());
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getField("CODE"));
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getField("NOM"));
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getField("CODE_BARRE"));
            Where wMatchingCodeF = new Where(this.tableArticleFournisseur.getField("CODE"), "=", aText);
            wMatchingCodeF = wMatchingCodeF.or(new Where(this.tableArticleFournisseur.getField("CODE_BARRE"), "=", aText));
            wMatchingCodeF = wMatchingCodeF.or(new Where(this.tableArticleFournisseur.getField("NOM"), "=", aText));
            selMatchingCodeF.setWhere(wMatchingCodeF.and(wNotSync));
            listSel.add(selMatchingCodeF);

            // CODE ARTICLE_FOURNISSEUR LIKE %aText% with limit
            SQLSelect selContainsCodeF = new SQLSelect();
            // selContainsCodeF.addSelectStar(this.tableArticleFournisseur);
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getKey());
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getField("CODE"));
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getField("NOM"));
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getField("CODE_BARRE"));
            Where wContainsCodeF = new Where(this.tableArticleFournisseur.getField("CODE"), "LIKE", "%" + aText + "%");
            wContainsCodeF = wContainsCodeF.or(new Where(this.tableArticleFournisseur.getField("CODE_BARRE"), "LIKE", "%" + aText + "%"));
            wContainsCodeF = wContainsCodeF.or(new Where(this.tableArticleFournisseur.getField("NOM"), "LIKE", "%" + aText + "%"));
            selContainsCodeF.setWhere(wContainsCodeF.and(wMatchingCodeF.not()).and(wNotSync));
            selContainsCodeF.setLimit(SQL_RESULT_LIMIT);

            listSel.add(selContainsCodeF);

            MultipleSQLSelectExecutor mult = new MultipleSQLSelectExecutor(this.tableArticle.getDBSystemRoot(), listSel);

            List<List<? extends SQLRowAccessor>> resultList = new ArrayList<>();
            resultList.addAll(mult.execute());

            // Recherche dans les codes fournisseurs
            SQLTable tableCodeArt = this.tableArticle.getDBRoot().getTable("CODE_FOURNISSEUR");
            SQLRowValues rowValsCodeF = new SQLRowValues(tableCodeArt);
            rowValsCodeF.putNulls("CODE");
            final SQLRowValues putRowValuesArt = rowValsCodeF.putRowValues("ID_ARTICLE");
            putRowValuesArt.putNulls(this.tableArticle.getFieldsName());
            if (this.hasDeclinaison) {
                for (String fieldName : this.tableArticle.getFieldsName()) {
                    if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
                        putRowValuesArt.putRowValues(fieldName).putNulls("NOM");
                    }
                }
            }
            final String codeText = aText;
            SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowValsCodeF);
            fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
                @Override
                public SQLSelect transformChecked(SQLSelect input) {

                    Where wCodeFContains = new Where(tableCodeArt.getField("CODE"), "LIKE", "%" + codeText + "%");
                    input.setWhere(wCodeFContains);
                    input.setLimit(SQL_RESULT_LIMIT);
                    return input;
                }
            });
            List<SQLRowValues> resultCodeF = fetcher.fetch();
            resultList.add(2, resultCodeF);

            Map<String, Map<Number, SQLRowAccessor>> mapDecl = new HashMap<>();
            for (List<? extends SQLRowAccessor> list : resultList) {

                if (!list.isEmpty()) {
                    if (list.get(0).getTable().getName().startsWith("ARTICLE_DECLINAISON")) {
                        Map<Number, SQLRowAccessor> map = new HashMap<>();
                        for (SQLRowAccessor sqlRow : list) {
                            map.put(sqlRow.getIDNumber(), sqlRow);
                        }
                        mapDecl.put("ID_" + list.get(0).getTable().getName(), map);
                    } else {
                        for (SQLRowAccessor sqlRow : list) {

                            StringBuffer buf = new StringBuffer();
                            if (sqlRow.getTable().getName().equals("CODE_FOURNISSEUR")) {
                                SQLRowAccessor rArt = sqlRow.getForeign("ID_ARTICLE");
                                buf.append(sqlRow.getString("CODE") + " -- ");
                                buf.append(rArt.getString("CODE") + " -- ");
                                buf.append(rArt.getString("NOM"));
                                if (this.hasDeclinaison) {
                                    for (String fieldName : rArt.getFields()) {
                                        if (fieldName.startsWith("ID_ARTICLE_DECLINAISON")) {
                                            Number rowDecl = rArt.getObject(fieldName) == null ? null : rArt.getNonEmptyForeignIDNumber(fieldName);
                                            if (rowDecl != null) {
                                                final Map<Number, SQLRowAccessor> mapIdDecl = mapDecl.get(fieldName);
                                                if (mapIdDecl != null) {
                                                    final SQLRowAccessor sqlRowAccessor = mapIdDecl.get(rowDecl);
                                                    if (sqlRowAccessor != null) {
                                                        buf.append(" -- " + sqlRowAccessor.getString("NOM"));
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }

                                result.add(new IComboSelectionItem(rArt, buf.toString()));
                            } else {
                                if (sqlRow.getString("CODE_BARRE") != null && sqlRow.getString("CODE_BARRE").trim().length() > 0) {
                                    buf.append(sqlRow.getString("CODE_BARRE") + " -- ");
                                }
                                buf.append(sqlRow.getString("CODE") + " -- ");
                                buf.append(sqlRow.getString("NOM"));
                                if (this.hasDeclinaison) {
                                    for (String fieldName : sqlRow.getFields()) {
                                        if (fieldName.startsWith("ID_ARTICLE_DECLINAISON")) {
                                            Number rowDecl = sqlRow.getObject(fieldName) == null ? null : sqlRow.getNonEmptyForeignIDNumber(fieldName);
                                            if (rowDecl != null) {
                                                final Map<Number, SQLRowAccessor> mapIdDecl = mapDecl.get(fieldName);
                                                if (mapIdDecl != null) {
                                                    final SQLRowAccessor sqlRowAccessor = mapIdDecl.get(rowDecl);
                                                    if (sqlRowAccessor != null) {
                                                        buf.append(" -- " + sqlRowAccessor.getString("NOM"));
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                result.add(new IComboSelectionItem(sqlRow, buf.toString()));
                            }
                        }
                    }
                }
            }

        }

        return result;
    }

    private void updateAutoCompletion(boolean autoselectIfMatch) {
        this.autoselectIfMatch = autoselectIfMatch;
        this.autoCheckDelay = PAUSE_MS * 2;
        synchronized (searchStack) {
            this.searchStack.push(this.text.getText().trim());
        }
    }

    private void loadAutoCompletion() {
        if (!this.isCompletionEnabled() || this.isLoading) {
            return;
        }
        final String t;
        synchronized (searchStack) {
            if (this.searchStack.isEmpty()) {
                return;
            }
            t = this.searchStack.pop();
            this.searchStack.clear();
        }

        final SwingWorker<List<IComboSelectionItem>, Object> worker = new SwingWorker<List<IComboSelectionItem>, Object>() {

            @Override
            protected List<IComboSelectionItem> doInBackground() throws Exception {
                List<IComboSelectionItem> l = getPossibleValues(t); // Liste de IComboSelection
                return l;
            }

            @Override
            protected void done() {
                List<IComboSelectionItem> l;
                try {
                    l = get();
                } catch (Exception e) {
                    l = new ArrayList<IComboSelectionItem>(0);
                    e.printStackTrace();
                }

                // On cache la popup si le nombre de ligne change afin que sa taille soit correcte
                if (l.size() != model.getSize() && l.size() <= ITextWithCompletionPopUp.MAXROW) {
                    hidePopup();
                }
                // on vide le model
                model.removeAllElements();
                model.addAll(l);

                if (l.size() > 0) {
                    showPopup();
                } else {
                    hidePopup();
                }
                SQLRowAccessor newRow = selectedRow;
                IComboSelectionItem newSelectedItem = null;
                boolean found = false;
                for (Iterator<IComboSelectionItem> iter = l.iterator(); iter.hasNext();) {
                    IComboSelectionItem element = iter.next();

                    if ((element.getRow().getString("CODE_BARRE").toLowerCase().equals(t.toLowerCase()) || element.getRow().getString("CODE").toLowerCase().equals(t.toLowerCase()))
                            && autoselectIfMatch) {
                        newRow = element.getRow();
                        newSelectedItem = element;
                        hidePopup();
                        found = true;
                        break;
                    }
                }
                if (selectAuto && found && !CompareUtils.equals(newRow, selectedRow)) {
                    final IComboSelectionItem selectedItem = newSelectedItem;
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            itemSelected(selectedItem);
                        }
                    });
                }
                if (!found) {
                    selectedRow = null;
                    itemSelected(null);
                }

            }
        };
        worker.execute();

    }

    public synchronized void hidePopup() {
        this.popup.setVisible(false);
    }

    private synchronized void showPopup() {
        if (this.model.getSize() > 0) {
            if (this.popupInvoker.isShowing()) {

                String max = "";
                for (IComboSelectionItem item : this.model.getList()) {
                    if (max.length() < item.getLabel().length()) {
                        max = item.getLabel();
                    }
                }
                final int stringWidth = this.text.getGraphics().getFontMetrics().stringWidth(max);
                this.popup.setMinWith(Math.max(450, stringWidth + 20));

                this.popup.show(this.popupInvoker, 0, this.text.getBounds().height);
            }
        }
    }

    public void changedUpdate(DocumentEvent e) {
        updateAutoCompletion(false);
        this.supp.firePropertyChange("value", null, this.getText());
    }

    public void insertUpdate(DocumentEvent e) {
        updateAutoCompletion(false);
        this.supp.firePropertyChange("value", null, this.getText());
    }

    public void removeUpdate(DocumentEvent e) {
        updateAutoCompletion(false);
        this.supp.firePropertyChange("value", null, this.getText());
    }

    public SQLRowAccessor getSelectedRow() {
        return this.selectedRow;
    }

    public void setSelectedRow(SQLRowAccessor row) {
        this.selectedRow = row;
    }

    private void clearText() {
        setText("");
    }

    public void setEditable(boolean b) {
        this.text.setEditable(b);
    }

    public void setFillWithField(String s) {
        this.fillWith = s;
    }

    public SQLField getFillWithField() {
        return this.tableArticle.getField(fillWith);
    }

    public void selectItem(IComboSelectionItem item) {
        if (!SwingUtilities.isEventDispatchThread()) {
            throw new IllegalStateException("Not in Swing!");
        }
        if (item != null) {
            if (this.fillWith != null) {
                // FIXME SQL request in Swing
                SQLRowAccessor row = item.getRow();
                this.setText(row.getObject(this.fillWith).toString());
            } else {
                this.setText(item.getLabel());
            }
        } else {
            this.clearText();
        }
        hidePopup();
    }

    public void setText(final String label) {
        if (!SwingUtilities.isEventDispatchThread()) {
            throw new IllegalStateException("Not in Swing!");
        }
        setCompletionEnabled(false);
        this.text.setText(label);
        if (label != null) {
            this.text.setCaretPosition(label.length());
        }
        this.text.repaint();
        setCompletionEnabled(true);
    }

    // Gestion des listeners de selection d'id
    public void addSelectionListener(SelectionRowListener l) {
        this.listeners.add(l);
    }

    public void removeSelectionListener(SelectionRowListener l) {
        this.listeners.remove(l);
    }

    private boolean isDispatching = false;

    private void fireSelectionRow(SQLRowAccessor row) {
        if (!this.isDispatching) {

            this.isDispatching = true;
            for (Iterator<SelectionRowListener> iter = this.listeners.iterator(); iter.hasNext();) {
                SelectionRowListener element = iter.next();
                element.rowSelected(row, this);
            }
            this.isDispatching = false;
        }
    }

    /**
     * @return Returns the completionEnabled.
     */
    boolean isCompletionEnabled() {
        return this.completionEnabled;
    }

    /**
     * @param completionEnabled The completionEnabled to set.
     */
    void setCompletionEnabled(boolean completionEnabled) {
        this.completionEnabled = completionEnabled;
    }

    public Object getText() {
        return this.text.getText();
    }

    /**
     * @param popupInvoker The popupInvoker to set.
     */
    public void setPopupInvoker(Component popupInvoker) {
        this.popupInvoker = popupInvoker;
    }

    public JTextComponent getTextComp() {
        return this.text;
    }

    public JComponent getComp() {
        return this;
    }

    public void setSelectionAutoEnabled(boolean b) {
        this.selectAuto = b;
    }

    public void setLimitedSize(int nbChar) {
        // rm previous ones
        final DocumentFilterList dfl = DocumentFilterList.get((AbstractDocument) this.text.getDocument());
        final Iterator<DocumentFilter> iter = dfl.getFilters().iterator();
        while (iter.hasNext()) {
            final DocumentFilter df = iter.next();
            if (df instanceof LimitedSizeDocumentFilter)
                iter.remove();
        }
        // add the new one
        DocumentFilterList.add((AbstractDocument) this.text.getDocument(), new LimitedSizeDocumentFilter(nbChar), FilterType.SIMPLE_FILTER);
    }

    @Override
    public void resetValue() {
        this.setText("");
    }

    @Override
    public void setValue(String val) {
        this.setText(val);
    }

    @Override
    public void addValueListener(PropertyChangeListener l) {
        this.supp.addPropertyChangeListener(l);
    }

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

    @Override
    public void rmValueListener(PropertyChangeListener l) {
        this.supp.removePropertyChangeListener(l);
    }

    @Override
    public void itemSelected(IComboSelectionItem item) {
        if (item == null) {
            fireSelectionRow(null);
        } else {
            final SQLRowAccessor row = item.getRow();
            if (this.isLoading) {
                this.rowToSelect = row;

            } else {
                if (!CompareUtils.equals(this.selectedRow, row)) {
                    this.setSelectedRow(row);
                    this.selectItem(item);
                    this.fireSelectionRow(row);
                }
            }
        }
    }

}