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 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 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.erp.core.sales.pos.io;

import org.openconcerto.erp.core.sales.pos.ui.BarcodeListener;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.component.ITextArea;
import org.openconcerto.utils.StringUtils;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * Lecteur code barres, intercepte les événements clavier pour détecter un scan de code. Le code
 * barre doit terminer par un retour à la ligne.
 */
public class BarcodeReader implements KeyEventDispatcher {

    public int maxInterKeyDelay = 80;
    private static final int MIN_BARCODE_LENGTH = 2;
    private final List<BarcodeListener> listeners = new ArrayList<BarcodeListener>(1);
    private String value = "";
    private final List<KeyEvent> eve = new ArrayList<KeyEvent>();
    private long firstTime = -1;
    private Timer timer;
    // non final car un TimerTask n'est pas reutilisable
    private TimerTask task;
    private boolean enable = true;
    private boolean debug = false;
    Map<Integer, String> mapCharacterFR = new HashMap<>();

    public BarcodeReader(int maxInterKeyDelay) {
        this.timer = null;
        this.task = null;
        this.maxInterKeyDelay = maxInterKeyDelay;
        this.mapCharacterFR.put((int) '&', "1");
        this.mapCharacterFR.put((int) 'é', "2");
        this.mapCharacterFR.put((int) '"', "3");
        this.mapCharacterFR.put((int) '\'', "4");
        this.mapCharacterFR.put((int) '(', "5");
        this.mapCharacterFR.put((int) '-', "6");
        this.mapCharacterFR.put((int) 'è', "7");
        this.mapCharacterFR.put((int) '_', "8");
        this.mapCharacterFR.put((int) 'ç', "9");
        this.mapCharacterFR.put((int) 'à', "0");
    }

    public synchronized void removeBarcodeListener(BarcodeListener l) {
        this.listeners.remove(l);
        if (this.listeners.isEmpty()) {
            stop();
        }
    }

    public synchronized void addBarcodeListener(final BarcodeListener l) {
        if (this.timer == null) {
            start();
        }
        this.listeners.add(l);
    }

    private void fire(String code) {
        for (int i = 0; i < this.listeners.size(); i++) {
            this.listeners.get(i).barcodeRead(code);
        }
    }

    /**
     * Commence à ecouter les évenements clavier pour intercepter les codes barres
     */

    public synchronized void start() {
        if (this.timer == null) {
            // init avant que les listeners s'en servent
            this.timer = new Timer(getClass().getName(), true);
            KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
            System.err.println("BarcodeReader start : scan delay " + this.maxInterKeyDelay + " ms");
        }
    }

    /**
     * Stoppe l'écoute sur les évenements clavier
     */
    public synchronized void stop() {
        if (this.timer != null) {
            System.err.println("BarcodeReader stop");
            this.timer.cancel();
            this.timer = null;
            KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
        }
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent e) {
        if (!this.enable) {
            return false;
        }
        if (this.task != null)
            this.task.cancel();

        final long t = e.getWhen();
        if (this.firstTime < 0) {
            this.firstTime = t;
        }
        int keyCode = e.getKeyCode();

        final long delay = t - this.firstTime;
        if (keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE || (delay > this.maxInterKeyDelay && keyCode != KeyEvent.VK_SHIFT)) {
            // touche normale
            if (this.debug) {
                System.err.println("Touche normale " + keyCode);
            }
            this.eve.add(e);
            redispatch();
            return true;
        }

        final char keyChar = e.getKeyChar();
        this.eve.add(e);
        if (e.getID() == KeyEvent.KEY_RELEASED) {
            if (keyCode == KeyEvent.VK_SHIFT) {
                // rien
                if (this.debug) {
                    System.err.println("SHIFT " + keyCode);
                }
            } else if (keyChar == ']') {
                this.value += keyChar;
                if (this.debug) {
                    System.err.println("]");
                }
            } else if (keyChar == '*' || keyChar == '$' || keyChar == '+' || keyChar == '/' || keyChar == '%' || keyChar == ' ') {
                this.value += keyChar;
                if (this.debug) {
                    System.err.println("KEY " + keyCode + " - " + keyChar);
                }
            } else if (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9 || keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z) {
                // from KeyEvent : same as ASCII
                if (this.debug) {
                    System.err.println("[0-9] [A-Z] " + keyCode + " : " + keyChar);
                }
                this.value += (char) keyCode;
            } else if (keyCode == KeyEvent.VK_ENTER && this.value.length() >= MIN_BARCODE_LENGTH) {
                // fin de code barre
                if (this.debug) {
                    System.err.println("BARCODE OK ENTER OR LENGHT " + keyCode + " length = " + this.value.length() + " min length =" + MIN_BARCODE_LENGTH);
                }
                this.value = this.value.trim();
                fire(this.value);
                reset();
            } else if (this.mapCharacterFR.containsKey((int) keyChar)) {
                if (this.debug) {
                    System.err.println("MAP DEFAULT FR CHAR " + keyChar + " WITH " + this.mapCharacterFR.get((int) keyChar));
                }
                this.value += this.mapCharacterFR.get((int) keyChar);
            } else if (Character.isLetter(keyChar) || Character.isDigit(keyChar)) {
                this.value += keyChar;
                if (this.debug) {
                    System.err.println("LETTER OR DIGIT " + keyChar);
                }
            } else if (keyChar == 29) {
                this.value += '\u001D';
                if (this.debug) {
                    System.err.println("<GS>");
                }
            } else if (keyChar == KeyEvent.CHAR_UNDEFINED) {
                System.err.println("CHAR_UNDEFINED");
            } else {
                // Caractere non code barre
                if (this.debug) {
                    System.err.println("CHAR NON CODE BARRE keyCode:" + keyCode + " keyChar:" + keyChar);
                }
                redispatch();
            }
            // lance un timer s'il reste des evenements non dispatchés
            if (!this.eve.isEmpty()) {
                this.firstTime = t;
                this.task = new TimerTask() {
                    @Override
                    public void run() {
                        redispatchLater();
                    }
                };
                this.timer.schedule(this.task, this.maxInterKeyDelay);
            }
            // si pas d'evenement, pas de temps associé
            assert !this.eve.isEmpty() || this.firstTime == -1;
        }
        return true;

    }

    private void redispatchLater() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                redispatch();
            }
        });
    }

    private void redispatch() {
        for (int i = 0; i < this.eve.size(); i++) {
            final KeyEvent ee = this.eve.get(i);
            KeyboardFocusManager.getCurrentKeyboardFocusManager().redispatchEvent(ee.getComponent(), ee);
            for (int j = 0; j < this.listeners.size(); j++) {
                this.listeners.get(j).keyReceived(ee);
            }
        }
        reset();
    }

    private void reset() {
        this.value = "";
        this.eve.clear();
        this.firstTime = -1;
    }

    public Map<Integer, String> getMapCharacterFR() {
        return this.mapCharacterFR;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    public static void main(String[] args) {
        String delay = "80";
        if (args.length > 0) {
            delay = args[0];
        }
        final int d = Integer.parseInt(delay);
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("BarCode reader");
                System.out.println("Using inter key delay: " + d);
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                JPanel panel = new JPanel();
                f.setTitle("Barcode reader test");
                f.setContentPane(panel);
                panel.setLayout(new GridBagLayout());
                GridBagConstraints c = new DefaultGridBagConstraints();
                final JLabel l = new JLabel("BarCode reader output :");
                panel.add(l, c);
                c.gridy++;
                c.weighty = 1;
                c.weightx = 1;
                c.fill = GridBagConstraints.BOTH;
                final ITextArea t1 = new ITextArea();
                panel.add(new JScrollPane(t1), c);

                BarcodeReader reader = new BarcodeReader(d);
                reader.setDebug(true);

                System.err.println("FR MAP");
                for (Entry<Integer, String> string : reader.getMapCharacterFR().entrySet()) {
                    System.err.println(string.getKey() + " --> " + string.getValue());
                }

                reader.addBarcodeListener(new BarcodeListener() {

                    @Override
                    public void keyReceived(KeyEvent ee) {
                        System.err.println("BarcodeReader keyReceived() : " + ee);
                    }

                    @Override
                    public void barcodeRead(String code) {
                        t1.append("Barcode OK : '" + code + "'\n");
                        t1.append("Hex: " + StringUtils.bytesToHexString(code.getBytes()));
                    }
                });

                f.setSize(new Dimension(640, 480));
                f.setLocationRelativeTo(null);
                f.setVisible(true);

            }
        });

    }

    public void setEnabled(boolean b) {
        this.enable = b;
    }
}