OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Rev 182 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
18 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
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.erp.core.sales.pos.io;
15
 
16
import org.openconcerto.erp.core.sales.pos.ui.BarcodeListener;
83 ilm 17
import org.openconcerto.ui.DefaultGridBagConstraints;
18
import org.openconcerto.ui.component.ITextArea;
180 ilm 19
import org.openconcerto.utils.StringUtils;
18 ilm 20
 
83 ilm 21
import java.awt.Dimension;
22
import java.awt.GridBagConstraints;
23
import java.awt.GridBagLayout;
18 ilm 24
import java.awt.KeyEventDispatcher;
25
import java.awt.KeyboardFocusManager;
26
import java.awt.event.KeyEvent;
27
import java.util.ArrayList;
156 ilm 28
import java.util.HashMap;
18 ilm 29
import java.util.List;
156 ilm 30
import java.util.Map;
31
import java.util.Map.Entry;
18 ilm 32
import java.util.Timer;
33
import java.util.TimerTask;
34
 
83 ilm 35
import javax.swing.JFrame;
36
import javax.swing.JLabel;
37
import javax.swing.JPanel;
38
import javax.swing.JScrollPane;
18 ilm 39
import javax.swing.SwingUtilities;
83 ilm 40
import javax.swing.UIManager;
18 ilm 41
 
42
/**
43
 * Lecteur code barres, intercepte les événements clavier pour détecter un scan de code. Le code
44
 * barre doit terminer par un retour à la ligne.
142 ilm 45
 */
18 ilm 46
public class BarcodeReader implements KeyEventDispatcher {
47
 
83 ilm 48
    public int maxInterKeyDelay = 80;
18 ilm 49
    private static final int MIN_BARCODE_LENGTH = 2;
50
    private final List<BarcodeListener> listeners = new ArrayList<BarcodeListener>(1);
51
    private String value = "";
52
    private final List<KeyEvent> eve = new ArrayList<KeyEvent>();
53
    private long firstTime = -1;
54
    private Timer timer;
55
    // non final car un TimerTask n'est pas reutilisable
56
    private TimerTask task;
142 ilm 57
    private boolean enable = true;
156 ilm 58
    private boolean debug = false;
59
    Map<Integer, String> mapCharacterFR = new HashMap<>();
18 ilm 60
 
83 ilm 61
    public BarcodeReader(int maxInterKeyDelay) {
18 ilm 62
        this.timer = null;
63
        this.task = null;
83 ilm 64
        this.maxInterKeyDelay = maxInterKeyDelay;
180 ilm 65
        this.mapCharacterFR.put((int) '&', "1");
66
        this.mapCharacterFR.put((int) 'é', "2");
67
        this.mapCharacterFR.put((int) '"', "3");
68
        this.mapCharacterFR.put((int) '\'', "4");
69
        this.mapCharacterFR.put((int) '(', "5");
70
        this.mapCharacterFR.put((int) '-', "6");
71
        this.mapCharacterFR.put((int) 'è', "7");
72
        this.mapCharacterFR.put((int) '_', "8");
73
        this.mapCharacterFR.put((int) 'ç', "9");
74
        this.mapCharacterFR.put((int) 'à', "0");
18 ilm 75
    }
76
 
77
    public synchronized void removeBarcodeListener(BarcodeListener l) {
78
        this.listeners.remove(l);
144 ilm 79
        if (this.listeners.isEmpty()) {
18 ilm 80
            stop();
81
        }
82
    }
83
 
84
    public synchronized void addBarcodeListener(final BarcodeListener l) {
144 ilm 85
        if (this.timer == null) {
86
            start();
87
        }
18 ilm 88
        this.listeners.add(l);
89
    }
90
 
91
    private void fire(String code) {
92
        for (int i = 0; i < this.listeners.size(); i++) {
93
            this.listeners.get(i).barcodeRead(code);
94
        }
95
    }
96
 
97
    /**
98
     * Commence à ecouter les évenements clavier pour intercepter les codes barres
142 ilm 99
     */
18 ilm 100
 
142 ilm 101
    public synchronized void start() {
102
        if (this.timer == null) {
103
            // init avant que les listeners s'en servent
104
            this.timer = new Timer(getClass().getName(), true);
105
            KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
180 ilm 106
            System.err.println("BarcodeReader start : scan delay " + this.maxInterKeyDelay + " ms");
142 ilm 107
        }
18 ilm 108
    }
109
 
110
    /**
111
     * Stoppe l'écoute sur les évenements clavier
142 ilm 112
     */
113
    public synchronized void stop() {
18 ilm 114
        if (this.timer != null) {
144 ilm 115
            System.err.println("BarcodeReader stop");
18 ilm 116
            this.timer.cancel();
93 ilm 117
            this.timer = null;
142 ilm 118
            KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
18 ilm 119
        }
120
    }
121
 
122
    @Override
123
    public boolean dispatchKeyEvent(KeyEvent e) {
180 ilm 124
        if (!this.enable) {
142 ilm 125
            return false;
180 ilm 126
        }
18 ilm 127
        if (this.task != null)
128
            this.task.cancel();
129
 
130
        final long t = e.getWhen();
131
        if (this.firstTime < 0) {
132
            this.firstTime = t;
133
        }
156 ilm 134
        int keyCode = e.getKeyCode();
18 ilm 135
 
142 ilm 136
        final long delay = t - this.firstTime;
180 ilm 137
        if (keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE || (delay > this.maxInterKeyDelay && keyCode != KeyEvent.VK_SHIFT)) {
18 ilm 138
            // touche normale
156 ilm 139
            if (this.debug) {
180 ilm 140
                System.err.println("Touche normale " + keyCode);
156 ilm 141
            }
142 ilm 142
            this.eve.add(e);
18 ilm 143
            redispatch();
142 ilm 144
            return true;
18 ilm 145
        }
146
 
156 ilm 147
        final char keyChar = e.getKeyChar();
18 ilm 148
        this.eve.add(e);
142 ilm 149
        if (e.getID() == KeyEvent.KEY_RELEASED) {
156 ilm 150
            if (keyCode == KeyEvent.VK_SHIFT) {
18 ilm 151
                // rien
156 ilm 152
                if (this.debug) {
153
                    System.err.println("SHIFT " + keyCode);
154
                }
180 ilm 155
            } else if (keyChar == ']') {
156
                this.value += keyChar;
157
                if (this.debug) {
158
                    System.err.println("]");
159
                }
156 ilm 160
            } else if (keyChar == '*' || keyChar == '$' || keyChar == '+' || keyChar == '/' || keyChar == '%' || keyChar == ' ') {
161
                this.value += keyChar;
162
                if (this.debug) {
163
                    System.err.println("KEY " + keyCode + " - " + keyChar);
164
                }
165
            } else if (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9 || keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z) {
18 ilm 166
                // from KeyEvent : same as ASCII
156 ilm 167
                if (this.debug) {
180 ilm 168
                    System.err.println("[0-9] [A-Z] " + keyCode + " : " + keyChar);
156 ilm 169
                }
170
                this.value += (char) keyCode;
171
            } else if (keyCode == KeyEvent.VK_ENTER && this.value.length() >= MIN_BARCODE_LENGTH) {
18 ilm 172
                // fin de code barre
156 ilm 173
                if (this.debug) {
174
                    System.err.println("BARCODE OK ENTER OR LENGHT " + keyCode + " length = " + this.value.length() + " min length =" + MIN_BARCODE_LENGTH);
175
                }
18 ilm 176
                this.value = this.value.trim();
177
                fire(this.value);
178
                reset();
180 ilm 179
            } else if (this.mapCharacterFR.containsKey((int) keyChar)) {
156 ilm 180
                if (this.debug) {
180 ilm 181
                    System.err.println("MAP DEFAULT FR CHAR " + keyChar + " WITH " + this.mapCharacterFR.get((int) keyChar));
156 ilm 182
                }
180 ilm 183
                this.value += this.mapCharacterFR.get((int) keyChar);
156 ilm 184
            } else if (Character.isLetter(keyChar) || Character.isDigit(keyChar)) {
185
                this.value += keyChar;
186
                if (this.debug) {
187
                    System.err.println("LETTER OR DIGIT " + keyChar);
188
                }
180 ilm 189
            } else if (keyChar == 29) {
190
                this.value += '\u001D';
191
                if (this.debug) {
192
                    System.err.println("<GS>");
193
                }
194
            } else if (keyChar == KeyEvent.CHAR_UNDEFINED) {
195
                System.err.println("CHAR_UNDEFINED");
18 ilm 196
            } else {
197
                // Caractere non code barre
156 ilm 198
                if (this.debug) {
180 ilm 199
                    System.err.println("CHAR NON CODE BARRE keyCode:" + keyCode + " keyChar:" + keyChar);
156 ilm 200
                }
18 ilm 201
                redispatch();
202
            }
203
            // lance un timer s'il reste des evenements non dispatchés
144 ilm 204
            if (!this.eve.isEmpty()) {
18 ilm 205
                this.firstTime = t;
206
                this.task = new TimerTask() {
207
                    @Override
208
                    public void run() {
209
                        redispatchLater();
210
                    }
211
                };
180 ilm 212
                this.timer.schedule(this.task, this.maxInterKeyDelay);
18 ilm 213
            }
214
            // si pas d'evenement, pas de temps associé
144 ilm 215
            assert !this.eve.isEmpty() || this.firstTime == -1;
18 ilm 216
        }
217
        return true;
180 ilm 218
 
18 ilm 219
    }
220
 
221
    private void redispatchLater() {
222
        SwingUtilities.invokeLater(new Runnable() {
223
            @Override
224
            public void run() {
225
                redispatch();
226
            }
227
        });
228
    }
229
 
230
    private void redispatch() {
231
        for (int i = 0; i < this.eve.size(); i++) {
232
            final KeyEvent ee = this.eve.get(i);
233
            KeyboardFocusManager.getCurrentKeyboardFocusManager().redispatchEvent(ee.getComponent(), ee);
234
            for (int j = 0; j < this.listeners.size(); j++) {
235
                this.listeners.get(j).keyReceived(ee);
236
            }
237
        }
238
        reset();
239
    }
240
 
241
    private void reset() {
242
        this.value = "";
243
        this.eve.clear();
244
        this.firstTime = -1;
245
    }
246
 
156 ilm 247
    public Map<Integer, String> getMapCharacterFR() {
180 ilm 248
        return this.mapCharacterFR;
156 ilm 249
    }
250
 
251
    public void setDebug(boolean debug) {
252
        this.debug = debug;
253
    }
254
 
83 ilm 255
    public static void main(String[] args) {
256
        String delay = "80";
257
        if (args.length > 0) {
258
            delay = args[0];
259
        }
260
        final int d = Integer.parseInt(delay);
261
        SwingUtilities.invokeLater(new Runnable() {
262
 
263
            @Override
264
            public void run() {
265
                try {
266
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
267
                } catch (Exception e) {
268
                    e.printStackTrace();
269
                }
270
                System.out.println("BarCode reader");
271
                System.out.println("Using inter key delay: " + d);
272
                JFrame f = new JFrame();
273
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
274
                JPanel panel = new JPanel();
275
                f.setTitle("Barcode reader test");
276
                f.setContentPane(panel);
277
                panel.setLayout(new GridBagLayout());
278
                GridBagConstraints c = new DefaultGridBagConstraints();
279
                final JLabel l = new JLabel("BarCode reader output :");
280
                panel.add(l, c);
281
                c.gridy++;
282
                c.weighty = 1;
283
                c.weightx = 1;
284
                c.fill = GridBagConstraints.BOTH;
285
                final ITextArea t1 = new ITextArea();
286
                panel.add(new JScrollPane(t1), c);
287
 
288
                BarcodeReader reader = new BarcodeReader(d);
156 ilm 289
                reader.setDebug(true);
290
 
291
                System.err.println("FR MAP");
292
                for (Entry<Integer, String> string : reader.getMapCharacterFR().entrySet()) {
293
                    System.err.println(string.getKey() + " --> " + string.getValue());
294
                }
295
 
83 ilm 296
                reader.addBarcodeListener(new BarcodeListener() {
297
 
298
                    @Override
299
                    public void keyReceived(KeyEvent ee) {
300
                        System.err.println("BarcodeReader keyReceived() : " + ee);
301
                    }
302
 
303
                    @Override
304
                    public void barcodeRead(String code) {
305
                        t1.append("Barcode OK : '" + code + "'\n");
180 ilm 306
                        t1.append("Hex: " + StringUtils.bytesToHexString(code.getBytes()));
83 ilm 307
                    }
308
                });
309
 
310
                f.setSize(new Dimension(640, 480));
311
                f.setLocationRelativeTo(null);
312
                f.setVisible(true);
313
 
314
            }
315
        });
316
 
317
    }
142 ilm 318
 
319
    public void setEnabled(boolean b) {
320
        this.enable = b;
321
    }
18 ilm 322
}