OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

Rev Author Line No. Line
83 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
83 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.sql.sqlobject;
15
 
16
import org.openconcerto.sql.model.SQLField;
17
import org.openconcerto.sql.model.SQLRowAccessor;
180 ilm 18
import org.openconcerto.sql.model.SQLRowValues;
19
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
83 ilm 20
import org.openconcerto.sql.model.SQLSelect;
21
import org.openconcerto.sql.model.SQLTable;
22
import org.openconcerto.sql.model.Where;
23
import org.openconcerto.sql.request.MultipleSQLSelectExecutor;
24
import org.openconcerto.ui.component.text.TextComponent;
25
import org.openconcerto.utils.CompareUtils;
26
import org.openconcerto.utils.OrderedSet;
132 ilm 27
import org.openconcerto.utils.cc.ITransformer;
83 ilm 28
import org.openconcerto.utils.checks.MutableValueObject;
29
import org.openconcerto.utils.model.DefaultIMutableListModel;
30
import org.openconcerto.utils.text.DocumentFilterList;
31
import org.openconcerto.utils.text.DocumentFilterList.FilterType;
32
import org.openconcerto.utils.text.LimitedSizeDocumentFilter;
33
 
34
import java.awt.Component;
35
import java.awt.GridLayout;
36
import java.awt.event.ComponentEvent;
37
import java.awt.event.ComponentListener;
38
import java.awt.event.FocusEvent;
39
import java.awt.event.FocusListener;
40
import java.awt.event.KeyEvent;
41
import java.awt.event.KeyListener;
42
import java.beans.PropertyChangeListener;
43
import java.beans.PropertyChangeSupport;
44
import java.sql.SQLException;
45
import java.util.ArrayList;
182 ilm 46
import java.util.HashMap;
83 ilm 47
import java.util.Iterator;
48
import java.util.List;
182 ilm 49
import java.util.Map;
83 ilm 50
import java.util.Stack;
51
import java.util.Vector;
52
 
53
import javax.swing.JComponent;
54
import javax.swing.JPanel;
55
import javax.swing.JTextField;
56
import javax.swing.SwingUtilities;
144 ilm 57
import javax.swing.SwingWorker;
83 ilm 58
import javax.swing.event.DocumentEvent;
59
import javax.swing.event.DocumentListener;
60
import javax.swing.text.AbstractDocument;
61
import javax.swing.text.DocumentFilter;
62
import javax.swing.text.JTextComponent;
63
 
64
public class ITextArticleWithCompletion extends JPanel implements DocumentListener, TextComponent, MutableValueObject<String>, IComboSelectionItemListener {
65
 
66
    public static int SQL_RESULT_LIMIT = 50;
67
 
68
    public static final int MODE_STARTWITH = 1;
69
    public static final int MODE_CONTAINS = 2;
70
 
71
    private JTextComponent text;
72
 
73
    private DefaultIMutableListModel<IComboSelectionItem> model = new DefaultIMutableListModel<IComboSelectionItem>();
74
 
75
    private boolean completionEnabled = true;
76
 
77
    private SQLRowAccessor selectedRow = null;
78
 
79
    private boolean selectAuto = true;
80
 
81
    protected ITextWithCompletionPopUp popup;
82
 
83
    OrderedSet<SelectionRowListener> listeners = new OrderedSet<SelectionRowListener>();
84
    Component popupInvoker;
85
 
86
    private boolean isLoading = false;
87
    private SQLRowAccessor rowToSelect = null;
88
 
89
    private String fillWith = "CODE";
90
    private final PropertyChangeSupport supp;
91
 
92
    private final SQLTable tableArticle, tableArticleFournisseur;
93
 
94
    // Asynchronous filling
95
    private Thread searchThread;
96
    private int autoCheckDelay = 1000;
97
    private boolean disposed = false;
98
    private Stack<String> searchStack = new Stack<String>();
99
    private boolean autoselectIfMatch;
100
    private static final int PAUSE_MS = 150;
182 ilm 101
    private final boolean hasDeclinaison;
83 ilm 102
 
182 ilm 103
    public ITextArticleWithCompletion(SQLTable tableArticle, SQLTable tableARticleFournisseur, boolean withDeclinaison) {
83 ilm 104
        this.tableArticle = tableArticle;
105
        this.tableArticleFournisseur = tableARticleFournisseur;
182 ilm 106
        this.hasDeclinaison = withDeclinaison;
83 ilm 107
        this.supp = new PropertyChangeSupport(this);
108
        this.popup = new ITextWithCompletionPopUp(this.model, this);
182 ilm 109
        this.popup.setMinWith(450);
83 ilm 110
        this.text = new JTextField();
111
        this.setLayout(new GridLayout(1, 1));
112
        this.add(this.text);
113
        setTextEditor(this.text);
114
        setPopupInvoker(this);
115
 
116
        //
117
        disposed = false;
118
        searchThread = new Thread() {
119
            public void run() {
120
                while (!disposed) {
121
                    if (autoCheckDelay == 0) {
122
                        autoCheckDelay = -1;
123
                        SwingUtilities.invokeLater(new Runnable() {
124
                            @Override
125
                            public void run() {
126
                                loadAutoCompletion();
127
                            }
128
                        });
129
 
130
                    } else if (autoCheckDelay > 0) {
131
                        autoCheckDelay -= PAUSE_MS;
132
                    }
133
                    try {
134
                        Thread.sleep(PAUSE_MS);
135
                    } catch (InterruptedException e) {
136
                        e.printStackTrace();
137
                    }
138
                }
139
 
140
            };
141
        };
142
        searchThread.setName("ITextArticleWithCompletion thread");
143
        searchThread.setPriority(Thread.MIN_PRIORITY);
144
        searchThread.setDaemon(true);
145
        searchThread.start();
146
 
147
    }
148
 
149
    public void setPopupListEnabled(boolean b) {
150
        this.popup.setListEnabled(b);
151
    }
152
 
94 ilm 153
    private Where whereAdditionnal;
132 ilm 154
    private ITransformer<SQLSelect, SQLSelect> selTransformer;
94 ilm 155
 
156
    public void setWhere(Where w) {
157
        this.whereAdditionnal = w;
158
    }
159
 
132 ilm 160
    public void setSelectTransformer(ITransformer<SQLSelect, SQLSelect> selTransformer) {
161
        this.selTransformer = selTransformer;
162
    }
163
 
83 ilm 164
    public void setTextEditor(final JTextComponent atext) {
165
        if (atext == null) {
166
            throw new IllegalArgumentException("null textEditor");
167
        }
168
        this.text = atext;
169
        atext.getDocument().addDocumentListener(this);
170
        atext.addKeyListener(new KeyListener() {
171
 
172
            private boolean consume;
173
 
174
            public void keyPressed(KeyEvent e) {
175
                if (e.getKeyCode() == KeyEvent.VK_TAB) {
176
                    // Complete si exactement la valeur souhaitée
177
                    updateAutoCompletion(true);
178
                    e.consume();
94 ilm 179
 
83 ilm 180
                } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
181
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
182
                        ITextArticleWithCompletion.this.popup.selectNext();
183
                        e.consume();
184
                    } else {
185
                        if (getSelectedRow() == null) {
186
                            // updateAutoCompletion();
187
                            showPopup();
188
                        }
189
                    }
190
 
191
                } else if (e.getKeyCode() == KeyEvent.VK_UP) {
192
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
193
                        ITextArticleWithCompletion.this.popup.selectPrevious();
194
                        e.consume();
195
                    }
196
 
197
                } else if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB) {
198
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
199
                        ITextArticleWithCompletion.this.popup.validateSelection();
200
                        e.consume();
201
                    } else {
202
                        autoselectIfMatch = true;
203
                        e.consume();
204
                    }
205
                } else if (e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
206
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
207
                        ITextArticleWithCompletion.this.popup.selectNextPage();
208
                        e.consume();
209
                    }
210
                } else if (e.getKeyCode() == KeyEvent.VK_PAGE_UP) {
211
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
212
                        ITextArticleWithCompletion.this.popup.selectPreviousPage();
213
                        e.consume();
214
                    }
215
                } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
216
                    if (ITextArticleWithCompletion.this.popup.isShowing()) {
217
                        hidePopup();
218
                    }
219
 
220
                }
221
 
222
                // else {
223
                // if (e.getKeyCode() != KeyEvent.VK_RIGHT && e.getKeyCode() !=
224
                // KeyEvent.VK_LEFT) {
225
                // fireSelectionId(-1);
226
                // }
227
                // }
228
                // Evite les bips
229
                if (ITextArticleWithCompletion.this.text.getDocument().getLength() == 0 && (e.getKeyCode() == KeyEvent.VK_DELETE || e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) {
230
                    System.err.println("consume");
231
                    this.consume = true;
232
                    e.consume();
233
                }
234
 
235
            }
236
 
237
            public void keyReleased(KeyEvent e) {
238
            }
239
 
240
            public void keyTyped(KeyEvent e) {
241
                // Evite les bips
242
                if (this.consume) {
243
                    e.consume();
244
                    this.consume = false;
245
                }
246
            }
247
        });
248
        this.addComponentListener(new ComponentListener() {
249
            public void componentHidden(ComponentEvent e) {
250
            }
251
 
252
            public void componentMoved(ComponentEvent e) {
253
            }
254
 
255
            public void componentResized(ComponentEvent e) {
256
                // ajuste la taille min de la popup
257
                ITextArticleWithCompletion.this.popup.setMinWith(atext.getBounds().width);
258
            }
259
 
260
            public void componentShown(ComponentEvent e) {
261
            }
262
        });
263
        atext.addFocusListener(new FocusListener() {
264
            @Override
265
            public void focusGained(FocusEvent e) {
266
 
267
            }
268
 
269
            @Override
270
            public void focusLost(FocusEvent e) {
271
                hidePopup();
272
            }
273
        });
274
    }
275
 
276
    /**
277
     * Retourne une liste de IComboSelectionItem, qui sont les selections possibles pour le text
278
     * passé
279
     *
280
     * @throws SQLException
281
     */
282
    List<IComboSelectionItem> getPossibleValues(String aText) throws SQLException {
283
        List<IComboSelectionItem> result = new Vector<IComboSelectionItem>();
284
        if (aText.isEmpty()) {
285
            return result;
286
        }
287
        aText = aText.trim();
288
 
289
        if (aText.length() > 0) {
290
 
291
            List<SQLSelect> listSel = new ArrayList<SQLSelect>();
182 ilm 292
 
83 ilm 293
            // CODE ARTICLE = aText
294
            SQLSelect selMatchingCode = new SQLSelect();
182 ilm 295
 
83 ilm 296
            selMatchingCode.addSelect(this.tableArticle.getKey());
297
            selMatchingCode.addSelect(this.tableArticle.getField("CODE"));
298
            selMatchingCode.addSelect(this.tableArticle.getField("NOM"));
299
            selMatchingCode.addSelect(this.tableArticle.getField("CODE_BARRE"));
182 ilm 300
            if (this.hasDeclinaison) {
301
                for (String fieldName : this.tableArticle.getFieldsName()) {
302
                    if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
303
                        selMatchingCode.addSelect(this.tableArticle.getField(fieldName));
304
 
305
                        SQLSelect selDecl = new SQLSelect();
306
                        SQLTable tableDecl = this.tableArticle.getForeignTable(fieldName);
307
                        selDecl.addSelect(tableDecl.getKey());
308
                        selDecl.addSelect(tableDecl.getField("NOM"));
309
                        listSel.add(selDecl);
310
                    }
311
                }
312
            }
83 ilm 313
            Where wMatchingCode = new Where(this.tableArticle.getField("CODE"), "=", aText);
314
            wMatchingCode = wMatchingCode.or(new Where(this.tableArticle.getField("NOM"), "=", aText));
315
            wMatchingCode = wMatchingCode.or(new Where(this.tableArticle.getField("CODE_BARRE"), "=", aText));
182 ilm 316
            wMatchingCode = wMatchingCode.and(new Where(this.tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));
94 ilm 317
            if (this.whereAdditionnal != null) {
318
                wMatchingCode = wMatchingCode.and(this.whereAdditionnal);
319
            }
83 ilm 320
            selMatchingCode.setWhere(wMatchingCode);
132 ilm 321
            if (this.selTransformer != null) {
322
                selMatchingCode = this.selTransformer.transformChecked(selMatchingCode);
323
            }
83 ilm 324
            listSel.add(selMatchingCode);
325
 
326
            // CODE ARTICLE LIKE %aText% with limit
327
            SQLSelect selContains = new SQLSelect();
328
            // selContains.addSelectStar(this.tableArticle);
329
            selContains.addSelect(this.tableArticle.getKey());
330
            selContains.addSelect(this.tableArticle.getField("CODE"));
331
            selContains.addSelect(this.tableArticle.getField("NOM"));
332
            selContains.addSelect(this.tableArticle.getField("CODE_BARRE"));
182 ilm 333
            if (this.hasDeclinaison) {
334
                for (String fieldName : this.tableArticle.getFieldsName()) {
335
                    if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
336
                        selContains.addSelect(this.tableArticle.getField(fieldName));
337
                    }
338
                }
339
            }
340
            Where wContains = new Where(this.tableArticle.getField("CODE"), "LIKE", "%" + aText + "%");
83 ilm 341
            wContains = wContains.or(new Where(this.tableArticle.getField("NOM"), "LIKE", "%" + aText + "%"));
342
            wContains = wContains.or(new Where(this.tableArticle.getField("CODE_BARRE"), "LIKE", "%" + aText + "%"));
182 ilm 343
            wContains = wContains.and(new Where(this.tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));
94 ilm 344
            if (this.whereAdditionnal != null) {
345
                wContains = wContains.and(this.whereAdditionnal);
346
            }
132 ilm 347
 
83 ilm 348
            selContains.setWhere(wContains.and(wMatchingCode.not()));
132 ilm 349
            selContains.setExcludeUndefined(false, this.tableArticle.getForeignTable("ID_STOCK"));
83 ilm 350
            selContains.setLimit(SQL_RESULT_LIMIT);
132 ilm 351
            if (this.selTransformer != null) {
352
                selContains = this.selTransformer.transformChecked(selContains);
353
            }
83 ilm 354
            listSel.add(selContains);
355
 
356
            // CODE ARTICLE = aText
94 ilm 357
            final Where wNotSync = new Where(this.tableArticleFournisseur.getField("ID_ARTICLE"), "IS", (Object) null)
358
                    .or(new Where(this.tableArticleFournisseur.getField("ID_ARTICLE"), "=", this.tableArticleFournisseur.getUndefinedID()));
83 ilm 359
 
360
            SQLSelect selMatchingCodeF = new SQLSelect();
361
            // selMatchingCodeF.addSelectStar(this.tableArticleFournisseur);
362
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getKey());
363
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getField("CODE"));
364
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getField("NOM"));
365
            selMatchingCodeF.addSelect(this.tableArticleFournisseur.getField("CODE_BARRE"));
366
            Where wMatchingCodeF = new Where(this.tableArticleFournisseur.getField("CODE"), "=", aText);
367
            wMatchingCodeF = wMatchingCodeF.or(new Where(this.tableArticleFournisseur.getField("CODE_BARRE"), "=", aText));
368
            wMatchingCodeF = wMatchingCodeF.or(new Where(this.tableArticleFournisseur.getField("NOM"), "=", aText));
369
            selMatchingCodeF.setWhere(wMatchingCodeF.and(wNotSync));
370
            listSel.add(selMatchingCodeF);
371
 
372
            // CODE ARTICLE_FOURNISSEUR LIKE %aText% with limit
373
            SQLSelect selContainsCodeF = new SQLSelect();
374
            // selContainsCodeF.addSelectStar(this.tableArticleFournisseur);
375
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getKey());
376
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getField("CODE"));
377
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getField("NOM"));
378
            selContainsCodeF.addSelect(this.tableArticleFournisseur.getField("CODE_BARRE"));
379
            Where wContainsCodeF = new Where(this.tableArticleFournisseur.getField("CODE"), "LIKE", "%" + aText + "%");
380
            wContainsCodeF = wContainsCodeF.or(new Where(this.tableArticleFournisseur.getField("CODE_BARRE"), "LIKE", "%" + aText + "%"));
381
            wContainsCodeF = wContainsCodeF.or(new Where(this.tableArticleFournisseur.getField("NOM"), "LIKE", "%" + aText + "%"));
382
            selContainsCodeF.setWhere(wContainsCodeF.and(wMatchingCodeF.not()).and(wNotSync));
383
            selContainsCodeF.setLimit(SQL_RESULT_LIMIT);
384
 
385
            listSel.add(selContainsCodeF);
386
 
387
            MultipleSQLSelectExecutor mult = new MultipleSQLSelectExecutor(this.tableArticle.getDBSystemRoot(), listSel);
388
 
180 ilm 389
            List<List<? extends SQLRowAccessor>> resultList = new ArrayList<>();
390
            resultList.addAll(mult.execute());
83 ilm 391
 
180 ilm 392
            // Recherche dans les codes fournisseurs
393
            SQLTable tableCodeArt = this.tableArticle.getDBRoot().getTable("CODE_FOURNISSEUR");
394
            SQLRowValues rowValsCodeF = new SQLRowValues(tableCodeArt);
395
            rowValsCodeF.putNulls("CODE");
182 ilm 396
            final SQLRowValues putRowValuesArt = rowValsCodeF.putRowValues("ID_ARTICLE");
397
            putRowValuesArt.putNulls(this.tableArticle.getFieldsName());
398
            if (this.hasDeclinaison) {
399
                for (String fieldName : this.tableArticle.getFieldsName()) {
400
                    if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
401
                        putRowValuesArt.putRowValues(fieldName).putNulls("NOM");
402
                    }
403
                }
404
            }
180 ilm 405
            final String codeText = aText;
406
            SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowValsCodeF);
407
            fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
408
                @Override
409
                public SQLSelect transformChecked(SQLSelect input) {
83 ilm 410
 
180 ilm 411
                    Where wCodeFContains = new Where(tableCodeArt.getField("CODE"), "LIKE", "%" + codeText + "%");
412
                    input.setWhere(wCodeFContains);
413
                    input.setLimit(SQL_RESULT_LIMIT);
414
                    return input;
415
                }
416
            });
417
            List<SQLRowValues> resultCodeF = fetcher.fetch();
418
            resultList.add(2, resultCodeF);
419
 
182 ilm 420
            Map<String, Map<Number, SQLRowAccessor>> mapDecl = new HashMap<>();
180 ilm 421
            for (List<? extends SQLRowAccessor> list : resultList) {
422
 
182 ilm 423
                if (!list.isEmpty()) {
424
                    if (list.get(0).getTable().getName().startsWith("ARTICLE_DECLINAISON")) {
425
                        Map<Number, SQLRowAccessor> map = new HashMap<>();
426
                        for (SQLRowAccessor sqlRow : list) {
427
                            map.put(sqlRow.getIDNumber(), sqlRow);
428
                        }
429
                        mapDecl.put("ID_" + list.get(0).getTable().getName(), map);
430
                    } else {
431
                        for (SQLRowAccessor sqlRow : list) {
180 ilm 432
 
182 ilm 433
                            StringBuffer buf = new StringBuffer();
434
                            if (sqlRow.getTable().getName().equals("CODE_FOURNISSEUR")) {
435
                                SQLRowAccessor rArt = sqlRow.getForeign("ID_ARTICLE");
436
                                buf.append(sqlRow.getString("CODE") + " -- ");
437
                                buf.append(rArt.getString("CODE") + " -- ");
438
                                buf.append(rArt.getString("NOM"));
439
                                if (this.hasDeclinaison) {
440
                                    for (String fieldName : rArt.getFields()) {
441
                                        if (fieldName.startsWith("ID_ARTICLE_DECLINAISON")) {
442
                                            Number rowDecl = rArt.getObject(fieldName) == null ? null : rArt.getNonEmptyForeignIDNumber(fieldName);
443
                                            if (rowDecl != null) {
444
                                                final Map<Number, SQLRowAccessor> mapIdDecl = mapDecl.get(fieldName);
445
                                                if (mapIdDecl != null) {
446
                                                    final SQLRowAccessor sqlRowAccessor = mapIdDecl.get(rowDecl);
447
                                                    if (sqlRowAccessor != null) {
448
                                                        buf.append(" -- " + sqlRowAccessor.getString("NOM"));
449
                                                    }
450
                                                }
451
                                            }
452
                                        }
453
                                    }
454
                                }
455
 
456
                                result.add(new IComboSelectionItem(rArt, buf.toString()));
457
                            } else {
458
                                if (sqlRow.getString("CODE_BARRE") != null && sqlRow.getString("CODE_BARRE").trim().length() > 0) {
459
                                    buf.append(sqlRow.getString("CODE_BARRE") + " -- ");
460
                                }
461
                                buf.append(sqlRow.getString("CODE") + " -- ");
462
                                buf.append(sqlRow.getString("NOM"));
463
                                if (this.hasDeclinaison) {
464
                                    for (String fieldName : sqlRow.getFields()) {
465
                                        if (fieldName.startsWith("ID_ARTICLE_DECLINAISON")) {
466
                                            Number rowDecl = sqlRow.getObject(fieldName) == null ? null : sqlRow.getNonEmptyForeignIDNumber(fieldName);
467
                                            if (rowDecl != null) {
468
                                                final Map<Number, SQLRowAccessor> mapIdDecl = mapDecl.get(fieldName);
469
                                                if (mapIdDecl != null) {
470
                                                    final SQLRowAccessor sqlRowAccessor = mapIdDecl.get(rowDecl);
471
                                                    if (sqlRowAccessor != null) {
472
                                                        buf.append(" -- " + sqlRowAccessor.getString("NOM"));
473
                                                    }
474
                                                }
475
                                            }
476
                                        }
477
                                    }
478
                                }
479
                                result.add(new IComboSelectionItem(sqlRow, buf.toString()));
480
                            }
180 ilm 481
                        }
83 ilm 482
                    }
483
                }
484
            }
485
 
486
        }
487
 
488
        return result;
489
    }
490
 
491
    private void updateAutoCompletion(boolean autoselectIfMatch) {
492
        this.autoselectIfMatch = autoselectIfMatch;
493
        this.autoCheckDelay = PAUSE_MS * 2;
494
        synchronized (searchStack) {
495
            this.searchStack.push(this.text.getText().trim());
496
        }
497
    }
498
 
499
    private void loadAutoCompletion() {
500
        if (!this.isCompletionEnabled() || this.isLoading) {
501
            return;
502
        }
503
        final String t;
504
        synchronized (searchStack) {
505
            if (this.searchStack.isEmpty()) {
506
                return;
507
            }
508
            t = this.searchStack.pop();
509
            this.searchStack.clear();
510
        }
511
 
144 ilm 512
        final SwingWorker<List<IComboSelectionItem>, Object> worker = new SwingWorker<List<IComboSelectionItem>, Object>() {
83 ilm 513
 
514
            @Override
515
            protected List<IComboSelectionItem> doInBackground() throws Exception {
516
                List<IComboSelectionItem> l = getPossibleValues(t); // Liste de IComboSelection
517
                return l;
518
            }
519
 
520
            @Override
521
            protected void done() {
522
                List<IComboSelectionItem> l;
523
                try {
524
                    l = get();
525
                } catch (Exception e) {
526
                    l = new ArrayList<IComboSelectionItem>(0);
527
                    e.printStackTrace();
528
                }
94 ilm 529
 
83 ilm 530
                // On cache la popup si le nombre de ligne change afin que sa taille soit correcte
531
                if (l.size() != model.getSize() && l.size() <= ITextWithCompletionPopUp.MAXROW) {
532
                    hidePopup();
533
                }
534
                // on vide le model
535
                model.removeAllElements();
536
                model.addAll(l);
537
 
538
                if (l.size() > 0) {
539
                    showPopup();
540
                } else {
541
                    hidePopup();
542
                }
543
                SQLRowAccessor newRow = selectedRow;
94 ilm 544
                IComboSelectionItem newSelectedItem = null;
83 ilm 545
                boolean found = false;
546
                for (Iterator<IComboSelectionItem> iter = l.iterator(); iter.hasNext();) {
547
                    IComboSelectionItem element = iter.next();
94 ilm 548
 
549
                    if ((element.getRow().getString("CODE_BARRE").toLowerCase().equals(t.toLowerCase()) || element.getRow().getString("CODE").toLowerCase().equals(t.toLowerCase()))
550
                            && autoselectIfMatch) {
83 ilm 551
                        newRow = element.getRow();
94 ilm 552
                        newSelectedItem = element;
83 ilm 553
                        hidePopup();
554
                        found = true;
555
                        break;
556
                    }
557
                }
558
                if (selectAuto && found && !CompareUtils.equals(newRow, selectedRow)) {
94 ilm 559
                    final IComboSelectionItem selectedItem = newSelectedItem;
83 ilm 560
                    SwingUtilities.invokeLater(new Runnable() {
561
                        public void run() {
94 ilm 562
                            itemSelected(selectedItem);
83 ilm 563
                        }
564
                    });
565
                }
566
                if (!found) {
567
                    selectedRow = null;
94 ilm 568
                    itemSelected(null);
83 ilm 569
                }
94 ilm 570
 
83 ilm 571
            }
572
        };
573
        worker.execute();
574
 
575
    }
576
 
577
    public synchronized void hidePopup() {
578
        this.popup.setVisible(false);
579
    }
580
 
581
    private synchronized void showPopup() {
582
        if (this.model.getSize() > 0) {
182 ilm 583
            if (this.popupInvoker.isShowing()) {
584
 
585
                String max = "";
586
                for (IComboSelectionItem item : this.model.getList()) {
587
                    if (max.length() < item.getLabel().length()) {
588
                        max = item.getLabel();
589
                    }
590
                }
591
                final int stringWidth = this.text.getGraphics().getFontMetrics().stringWidth(max);
592
                this.popup.setMinWith(Math.max(450, stringWidth + 20));
593
 
83 ilm 594
                this.popup.show(this.popupInvoker, 0, this.text.getBounds().height);
182 ilm 595
            }
83 ilm 596
        }
597
    }
598
 
599
    public void changedUpdate(DocumentEvent e) {
600
        updateAutoCompletion(false);
601
        this.supp.firePropertyChange("value", null, this.getText());
602
    }
603
 
604
    public void insertUpdate(DocumentEvent e) {
605
        updateAutoCompletion(false);
606
        this.supp.firePropertyChange("value", null, this.getText());
607
    }
608
 
609
    public void removeUpdate(DocumentEvent e) {
610
        updateAutoCompletion(false);
611
        this.supp.firePropertyChange("value", null, this.getText());
612
    }
613
 
614
    public SQLRowAccessor getSelectedRow() {
615
        return this.selectedRow;
616
    }
617
 
618
    public void setSelectedRow(SQLRowAccessor row) {
619
        this.selectedRow = row;
620
    }
621
 
622
    private void clearText() {
623
        setText("");
624
    }
625
 
626
    public void setEditable(boolean b) {
627
        this.text.setEditable(b);
628
    }
629
 
630
    public void setFillWithField(String s) {
631
        this.fillWith = s;
632
    }
633
 
634
    public SQLField getFillWithField() {
635
        return this.tableArticle.getField(fillWith);
636
    }
637
 
638
    public void selectItem(IComboSelectionItem item) {
639
        if (!SwingUtilities.isEventDispatchThread()) {
640
            throw new IllegalStateException("Not in Swing!");
641
        }
642
        if (item != null) {
643
            if (this.fillWith != null) {
644
                // FIXME SQL request in Swing
645
                SQLRowAccessor row = item.getRow();
646
                this.setText(row.getObject(this.fillWith).toString());
647
            } else {
648
                this.setText(item.getLabel());
649
            }
650
        } else {
651
            this.clearText();
652
        }
653
        hidePopup();
654
    }
655
 
656
    public void setText(final String label) {
657
        if (!SwingUtilities.isEventDispatchThread()) {
658
            throw new IllegalStateException("Not in Swing!");
659
        }
660
        setCompletionEnabled(false);
661
        this.text.setText(label);
662
        if (label != null) {
663
            this.text.setCaretPosition(label.length());
664
        }
665
        this.text.repaint();
666
        setCompletionEnabled(true);
667
    }
668
 
669
    // Gestion des listeners de selection d'id
670
    public void addSelectionListener(SelectionRowListener l) {
671
        this.listeners.add(l);
672
    }
673
 
674
    public void removeSelectionListener(SelectionRowListener l) {
675
        this.listeners.remove(l);
676
    }
677
 
678
    private boolean isDispatching = false;
679
 
680
    private void fireSelectionRow(SQLRowAccessor row) {
681
        if (!this.isDispatching) {
94 ilm 682
 
83 ilm 683
            this.isDispatching = true;
684
            for (Iterator<SelectionRowListener> iter = this.listeners.iterator(); iter.hasNext();) {
685
                SelectionRowListener element = iter.next();
686
                element.rowSelected(row, this);
687
            }
688
            this.isDispatching = false;
689
        }
690
    }
691
 
692
    /**
693
     * @return Returns the completionEnabled.
694
     */
695
    boolean isCompletionEnabled() {
696
        return this.completionEnabled;
697
    }
698
 
699
    /**
700
     * @param completionEnabled The completionEnabled to set.
701
     */
702
    void setCompletionEnabled(boolean completionEnabled) {
703
        this.completionEnabled = completionEnabled;
704
    }
705
 
706
    public Object getText() {
707
        return this.text.getText();
708
    }
709
 
710
    /**
711
     * @param popupInvoker The popupInvoker to set.
712
     */
713
    public void setPopupInvoker(Component popupInvoker) {
714
        this.popupInvoker = popupInvoker;
715
    }
716
 
717
    public JTextComponent getTextComp() {
718
        return this.text;
719
    }
720
 
721
    public JComponent getComp() {
722
        return this;
723
    }
724
 
725
    public void setSelectionAutoEnabled(boolean b) {
726
        this.selectAuto = b;
727
    }
728
 
729
    public void setLimitedSize(int nbChar) {
730
        // rm previous ones
731
        final DocumentFilterList dfl = DocumentFilterList.get((AbstractDocument) this.text.getDocument());
732
        final Iterator<DocumentFilter> iter = dfl.getFilters().iterator();
733
        while (iter.hasNext()) {
734
            final DocumentFilter df = iter.next();
735
            if (df instanceof LimitedSizeDocumentFilter)
736
                iter.remove();
737
        }
738
        // add the new one
739
        DocumentFilterList.add((AbstractDocument) this.text.getDocument(), new LimitedSizeDocumentFilter(nbChar), FilterType.SIMPLE_FILTER);
740
    }
741
 
742
    @Override
743
    public void resetValue() {
744
        this.setText("");
745
    }
746
 
747
    @Override
748
    public void setValue(String val) {
749
        this.setText(val);
750
    }
751
 
752
    @Override
753
    public void addValueListener(PropertyChangeListener l) {
754
        this.supp.addPropertyChangeListener(l);
755
    }
756
 
757
    @Override
758
    public String getValue() {
759
        return (String) this.getText();
760
    }
761
 
762
    @Override
763
    public void rmValueListener(PropertyChangeListener l) {
764
        this.supp.removePropertyChangeListener(l);
765
    }
766
 
767
    @Override
768
    public void itemSelected(IComboSelectionItem item) {
769
        if (item == null) {
770
            fireSelectionRow(null);
771
        } else {
772
            final SQLRowAccessor row = item.getRow();
773
            if (this.isLoading) {
774
                this.rowToSelect = row;
775
 
776
            } else {
777
                if (!CompareUtils.equals(this.selectedRow, row)) {
778
                    this.setSelectedRow(row);
779
                    this.selectItem(item);
780
                    this.fireSelectionRow(row);
781
                }
782
            }
783
        }
784
    }
785
 
786
}