working usb scanner support, missing config widget
authorKonrad Rosenbaum <konrad@silmor.de>
Sun, 19 Jan 2014 18:38:13 +0000 (19:38 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Sun, 19 Jan 2014 18:38:13 +0000 (19:38 +0100)
31 files changed:
plugins/bcs-usb/bcs-plugin.cpp [new file with mode: 0644]
plugins/bcs-usb/bcs-plugin.h [new file with mode: 0644]
plugins/bcs-usb/bcs-usb.pro [new file with mode: 0644]
plugins/bcs-usb/bcskeyboard.cpp [new file with mode: 0644]
plugins/bcs-usb/bcskeyboard.h [new file with mode: 0644]
plugins/bcs-usb/configwidget.cpp [new file with mode: 0644]
plugins/bcs-usb/configwidget.h [new file with mode: 0644]
plugins/bcs-usb/hidapi.pri [moved from plugins/bcscanner/hidapi.pri with 100% similarity]
plugins/bcs-usb/hidscanner.cpp [new file with mode: 0644]
plugins/bcs-usb/hidscanner.h [new file with mode: 0644]
plugins/bcs-usb/kbdlayouts/base.kbl [new file with mode: 0644]
plugins/bcs-usb/kbdlayouts/de.kbl [new file with mode: 0644]
plugins/bcs-usb/kbdlayouts/qwerty.kbl [new file with mode: 0644]
plugins/bcs-usb/kbdlayouts/qwertz.kbl [new file with mode: 0644]
plugins/bcs-usb/kbdlayouts/us.kbl [new file with mode: 0644]
plugins/bcs-usb/layouts.qrc [new file with mode: 0644]
plugins/bcscanner/bcs-plugin.cpp [deleted file]
plugins/bcscanner/bcs-plugin.h [deleted file]
plugins/bcscanner/bcscanner.pro [deleted file]
plugins/plugins.pro
src/main.cpp
src/main.h
src/misc/barcode-plugin.cpp
src/misc/barcode-plugin.h
src/misc/misc.pri
src/mwin/entrancetab.cpp
src/mwin/overview.cpp
src/mwin/overview.h
src/widgets/barcodeline.cpp [new file with mode: 0644]
src/widgets/barcodeline.h [new file with mode: 0644]
src/widgets/widgets.pri

diff --git a/plugins/bcs-usb/bcs-plugin.cpp b/plugins/bcs-usb/bcs-plugin.cpp
new file mode 100644 (file)
index 0000000..286c4af
--- /dev/null
@@ -0,0 +1,149 @@
+//
+// C++ Implementation: Barcode Plugin for USB
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "bcs-plugin.h"
+#include "configwidget.h"
+#include "hidscanner.h"
+
+#include <hidapi.h>
+
+#include <QDebug>
+#include <QSettings>
+#include <QTimer>
+
+MHidBarcodePlugin::MHidBarcodePlugin(): QObject()
+{
+        qDebug()<<"Initializing USB Barcode Plugin";
+        misopen = (hid_init()==0);
+        if(misopen)
+                qDebug()<<" ...success.";
+        else{
+                qDebug()<<" ...failed to initialize USB-HID.";
+                return;
+        } 
+        mdetecttmr=new QTimer(this);
+        connect(mdetecttmr,SIGNAL(timeout()),this,SLOT(detectScanners()));
+        mdetecttmr->setSingleShot(false);
+        restartDetect();
+}
+
+MHidBarcodePlugin::~MHidBarcodePlugin()
+{
+        qDebug()<<"Unloading USB Barcode Plugin";
+        if(mdetecttmr)mdetecttmr->stop();
+        for(MHidBarcodeScanner*sc:mscanners){
+                if(sc)delete sc;
+        }
+        mscanners.clear();
+        if(misopen)hid_exit();
+}
+
+
+void MHidBarcodePlugin::configure(MBarcodeConfiguration* cfg)
+{
+        //suspend detection until dialog closed
+        if(mdetecttmr)mdetecttmr->stop();
+        //main tab
+        new MUsbBarcodeConfig(cfg);
+        QSettings set;set.beginGroup(MUsbBarcodeConfig::settingsGroup);
+        for(const QString s:set.childGroups())
+                new MUsbScannerConfig(cfg,s);
+        //restart detection
+        connect(cfg,SIGNAL(destroyed(QObject*)),this,SLOT(restartDetect()));
+}
+
+MHidBarcodePlugin::Config::Config(QSettings& set,QString grp)
+{
+        set.beginGroup(grp);
+        isactive=set.value(MUsbBarcodeConfig::activeKey,false).toBool();
+        vendor=set.value(MUsbBarcodeConfig::vendorKey,0).toInt();
+        product=set.value(MUsbBarcodeConfig::productKey,0).toInt();
+        iface=set.value(MUsbBarcodeConfig::interfaceKey,0).toInt();
+        useserial=set.value(MUsbBarcodeConfig::useSerialKey,false).toBool();
+        serial=set.value(MUsbBarcodeConfig::serialKey).toString();
+        group=grp;
+        set.endGroup();
+}
+
+void MHidBarcodePlugin::restartDetect()
+{
+        if(!misopen){
+                if(mdetecttmr){
+                        delete mdetecttmr;
+                        mdetecttmr=0;
+                }
+                return;
+        }
+        const int interv=QSettings().value(MUsbBarcodeConfig::intervalKey,10).toInt();
+        if(interv>0)
+                mdetecttmr->start(interv*1000);
+        else
+                mdetecttmr->stop();
+        //re-read config
+        QSettings set;
+        set.beginGroup(MUsbBarcodeConfig::settingsGroup);
+        mconfig.clear();
+        for(const QString grp:set.childGroups()){
+                Config c(set,grp);
+                if(!c.isactive){
+                        continue;
+                }
+                mconfig.append(c);
+        }
+        //TODO: go through running scanners and kill unknowns
+}
+
+void MHidBarcodePlugin::detectScanners()
+{
+        if(!misopen)return;
+        qDebug()<<"scanning for scanners...";
+        hid_device_info *info=hid_enumerate(0,0);
+        for(hid_device_info*dev=info;dev;dev=dev->next){
+                //is it known?
+                Config cfg=findCfgMatch(dev->vendor_id,dev->product_id,dev->interface_number,
+                                        QString::fromWCharArray(dev->serial_number));
+                if(cfg.vendor<=0)continue;
+                //is it already open?
+                MHidBarcodeScanner*scanner=findScanner(dev->path);
+                if(scanner)continue;
+                //open it
+                qDebug()<<"instantiating scanner";
+                scanner=new MHidBarcodeScanner(dev->path,cfg.group);
+                mscanners.append(scanner);
+                connect(scanner,SIGNAL(destroyed(QObject*)),this,SLOT(removeScanner(QObject*)));
+        }
+        hid_free_enumeration(info);
+}
+
+MHidBarcodePlugin::Config MHidBarcodePlugin::findCfgMatch(int vendor, int product, int iface, QString serial)
+{
+        for(Config c:mconfig){
+                if(c.vendor!=vendor || c.product!=product || c.iface!=iface)continue;
+                if(c.useserial && c.serial!=serial)continue;
+                return c;
+        }
+        return Config();
+}
+
+MHidBarcodeScanner* MHidBarcodePlugin::findScanner(QString path)
+{
+        for(MHidBarcodeScanner*scanner:mscanners)
+                if(scanner->matchPath(path))
+                        return scanner;
+        return nullptr;
+}
+
+void MHidBarcodePlugin::removeScanner(QObject* o)
+{
+        MHidBarcodeScanner*s=qobject_cast< MHidBarcodeScanner* >(o);
+        if(s)
+                mscanners.removeAll(s);
+}
+
diff --git a/plugins/bcs-usb/bcs-plugin.h b/plugins/bcs-usb/bcs-plugin.h
new file mode 100644 (file)
index 0000000..47f97d4
--- /dev/null
@@ -0,0 +1,54 @@
+//
+// C++ Interface: plugin for USB barcode scanners
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_BARCODE_PLUGIN_HID_H
+#define MAGICSMOKE_BARCODE_PLUGIN_HID_H
+
+#include "barcode-plugin.h"
+
+class QTimer;
+class MHidBarcodeScanner;
+class QSettings;
+class MHidBarcodePlugin:public QObject,public MBarcodePlugin
+{
+        Q_OBJECT
+        Q_PLUGIN_METADATA(IID MBarcodePlugin_IID)
+        Q_INTERFACES(MBarcodePlugin)
+        public:
+                MHidBarcodePlugin();
+                virtual ~MHidBarcodePlugin();
+                virtual void configure(MBarcodeConfiguration*)override;
+        private slots:
+                void detectScanners();
+                void restartDetect();
+                void removeScanner(QObject*);
+        private:
+                bool misopen;
+                QTimer*mdetecttmr=nullptr;
+                struct Config{
+                        int vendor=0,product=0,iface=0;
+                        bool useserial=false,isactive=false;
+                        QString serial,group;
+                        Config(){}
+                        Config(const Config&)=default;
+                        Config(Config&&)=default;
+                        Config(QSettings&,QString);
+                        Config& operator=(const Config&)=default;
+                        Config& operator=(Config&&)=default;
+                };
+                QList<Config>mconfig;
+                Config findCfgMatch(int vendor,int product,int iface,QString serial);
+                QList<MHidBarcodeScanner*>mscanners;
+                MHidBarcodeScanner*findScanner(QString path);
+};
+
+#endif
diff --git a/plugins/bcs-usb/bcs-usb.pro b/plugins/bcs-usb/bcs-usb.pro
new file mode 100644 (file)
index 0000000..61de9c9
--- /dev/null
@@ -0,0 +1,12 @@
+TEMPLATE = lib
+TARGET = msp-bcs-usb
+CONFIG += plugin no_plugin_name_prefix
+
+include (../../basics.pri)
+include (../../src/smoke.pri)
+include (hidapi.pri)
+
+SOURCES += bcs-plugin.cpp configwidget.cpp bcskeyboard.cpp hidscanner.cpp
+HEADERS += bcs-plugin.h configwidget.h bcskeyboard.h hidscanner.h
+
+RESOURCES += layouts.qrc
\ No newline at end of file
diff --git a/plugins/bcs-usb/bcskeyboard.cpp b/plugins/bcs-usb/bcskeyboard.cpp
new file mode 100644 (file)
index 0000000..9546563
--- /dev/null
@@ -0,0 +1,336 @@
+//
+// C++ Implementation: Barcode Plugin for USB: Keyboard Stuff
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "bcskeyboard.h"
+#include <QFileInfo>
+#include <QCoreApplication>
+#include <QStringList>
+#include <QDebug>
+#include <QDir>
+
+MHidKeyLayout::MHidKeyLayout()
+{
+        loadLayout("base");
+}
+
+MHidKeyLayout::MHidKeyLayout(QString name)
+{
+        loadLayout(name);
+}
+
+QString MHidKeyLayout::layoutCodeName() const
+{
+        if(mfilename.isEmpty())return QString();
+        return QFileInfo(mfilename).baseName();
+}
+
+void MHidKeyLayout::clearKeyMapping()
+{
+        mkeymap.clear();
+}
+
+void MHidKeyLayout::resetLayout()
+{
+        clearKeyMapping();
+        loadLayout("base");
+}
+
+void MHidKeyLayout::setKeyMapping(unsigned char keycode, const QList<MKey> &c)
+{
+        if(!c.isEmpty())mkeymap.insert(keycode,c);
+        else mkeymap.remove(keycode);
+}
+
+QString MHidKeyLayout::toLayoutDoc() const
+{
+        QString ret="#This is a generated keyboard layout\n";
+        if(!mlname.isEmpty())ret+="title "+mlname+"\n";
+        for(unsigned char kc:mkeymap.keys()){
+                if(mkeymap[kc].isEmpty())continue;
+                ret+=QString("0x%1").arg((int)kc,0,16);
+                for(const MKey&k:mkeymap[kc])
+                        ret+=QString(" %1").arg(k.toCharName());
+                ret+="\n";
+        }
+        return ret;
+}
+
+bool MHidKeyLayout::loadLayout(QString filename)
+{
+        //no slash: try to find a match
+        if(!filename.contains('/')){
+                if(!filename.endsWith(".kbl"))filename+=".kbl";
+                //search for file in app dir
+                if(QFileInfo(QCoreApplication::applicationDirPath()+"/kbdlayouts/"+filename).exists())
+                        filename=QCoreApplication::applicationDirPath()+"/kbdlayouts/"+filename;
+                //search in resources
+                else if(QFileInfo(":/kbdlayouts/"+filename).exists())
+                        filename=":/kbdlayouts/"+filename;
+                else{
+                        qDebug()<<"Unable to find a match for keyboard layout"<<filename;
+                        return false;
+                }
+        }
+        //load file
+        QFile lf(filename);
+        if(!lf.open(QIODevice::ReadOnly)){
+                qDebug()<<"unable to open keyboard layout"<<filename<<"for USB HID scanners";
+                return false;
+        }
+        return parseLayout(QString::fromUtf8(lf.readAll()));
+}
+
+bool MHidKeyLayout::parseLayout(QString content,QString filename)
+{
+        mfilename.clear();
+        QString ntitle;
+        const QStringList clst=content.split('\n',QString::KeepEmptyParts);
+        for(int i=0;i<clst.size();i++){
+                QString line=clst[i].trimmed();
+                if(line.isEmpty())continue;
+                if(line.startsWith('#'))continue;
+                const QStringList toks=line.split(' ',QString::SkipEmptyParts);
+                if(toks.size()<2){
+                        qDebug()<<"Error: incomplete line"<<(i+1)<<"in layout file"<<filename;
+                        return false;
+                }
+                if(toks[0]=="title"){
+                        ntitle=line.mid(line.indexOf(' ')).trimmed();
+                        continue;
+                }
+                if(toks[0]=="include"){
+                        if(!loadLayout(toks[1]))return false;
+                        else continue;
+                }
+                bool ok;
+                const int kc=toks[0].toInt(&ok,0);
+                if(!ok || kc<0 || kc>255){
+                        qDebug()<<"Error: illegal key code"<<toks[0]<<"on line"<<(i+1)<<"in layout file"<<filename;
+                        return false;
+                }
+                QList<MKey>keys;
+                for(int i=1;i<toks.size();i++)
+                        keys.append(MKey::fromCharName(toks[i]));
+                if(keys.size()==1){
+                        keys[0]=keys[0].toLower();
+                        keys.append(keys[0].toUpper());
+                }
+                mkeymap.insert(kc,keys);
+        }
+        mfilename=filename;
+        mlname=ntitle;
+        return true;
+}
+
+MKeySequence MHidKeyLayout::toKeys(const MHidKeySequence& hsq) const
+{
+        MKeySequence ret;
+        for(MHidKeyEvent ev:hsq){
+                //get modifiers
+                MKey::Modifiers mod=ev.modifiers();
+                //get key
+                const int key=ev.lastkeycode();
+                //layer?
+                const int layer=mod.testFlag(MKey::AltGr)?2:(mod.testFlag(MKey::Shift)?1:0);
+                //generate key
+                ret.append(MKey(mkeymap.value(key).value(layer),mod));
+        }
+        return ret;
+}
+
+QString MHidKeyLayout::toString(const MHidKeySequence& hsq) const
+{
+        return toKeys(hsq).toString();
+}
+
+QMap< QString, QString > MHidKeyLayout::findLayouts()
+{
+        static QMap<QString,QString>laymap;
+        if(laymap.size()>0)return laymap;
+        //search all files
+        for(QString d:QStringList()<<":/kbdlayouts"<<QCoreApplication::applicationDirPath()+"/kbdlayouts")
+                for(QString fn:QDir(d).entryList(QStringList()<<"*.kbl",QDir::Files)){
+                        MHidKeyLayout kl(d+"/"+fn);
+                        if(!kl.layoutFileName().isEmpty())
+                                laymap.insert(kl.layoutCodeName(),kl.layoutName());
+                }
+        //done
+        return laymap;
+}
+
+MKey::MKey(QChar c, Modifiers m)
+        :mmod(m),mtype(CharKey),mchar(c)
+{
+        //is it space, return, tab?
+        if(c==' ')mtype=Space;else
+        if(c=='\n')mtype=Return;else
+        if(c=='\t')mtype=Tabulator;
+        //sort out shift modifier if this is a normal char
+        else if(m.testFlag(Shift)){
+                mmod^=Shift;
+                mchar=mchar.toUpper();
+        }else{
+                mchar=mchar.toLower();
+        }
+}
+
+MKey::MKey(const MKey& k, Modifiers m)
+        :mmod(k.mmod|m),mtype(k.mtype),mchar(k.mchar)
+{
+        //shift correction
+        if(mtype==CharKey){
+                if(mmod.testFlag(Shift)){
+                        mmod^=Shift;
+                        mchar=mchar.toUpper();
+                }else{
+                        mchar=mchar.toLower();
+                }
+        }
+}
+
+bool MKey::operator==(const MKey& k)
+{
+        //trivial: type and modifier check
+        if(mtype!=k.mtype || mmod!=k.mmod)return false;
+        //char compare?
+        if(mtype==CharKey)return mchar==k.mchar;
+        else return true;
+}
+
+QString MKey::toCharName() const
+{
+        QString ret;
+        //release?
+        if(mtype==AllKeyRelease)return "none";
+        //modifiers
+        if(mmod&Shift)ret+="shift-";
+        if(mmod&Ctrl)ret+="ctrl-";
+        if(mmod&Alt)ret+="alt-";
+        if(mmod&AltGr)ret+="altgr-";
+        //key
+        switch(mtype){
+                case Escape:return ret+"esc";
+                case CapsLock:return ret+"caps";
+                case NumLock:return ret+"num";
+                case Backspace:return ret+"backspace";
+                case Return:return ret+"return";
+                case Tabulator:return ret+"tab";
+                case Space:return ret+"space";
+                case CharKey:return ret+mchar;
+                default:return ret+"none";
+        }
+}
+
+MKey MKey::fromCharName(QString n)
+{
+        //separate modifiers
+        Modifiers mods=None;
+        if(n.contains('-')){
+                QStringList sl=n.split('-',QString::SkipEmptyParts);
+                n=sl.takeLast();
+                for(QString m:sl){
+                        m=m.toLower();
+                        if(m=="shift")mods|=Shift;
+                        else if(m=="ctrl")mods|=Ctrl;
+                        else if(m=="alt")mods|=Alt;
+                        else if(m=="altgr")mods|=AltGr;
+                        else qDebug()<<"Warning: illegal modifier"<<m;
+                }
+        }
+        //normal key?
+        if(n.size()==1)return MKey(n[0],mods);
+        //special key or alias...
+        n=n.toLower();
+        if(n=="minus")return MKey('-',mods);
+        if(n=="plus")return MKey('+',mods);
+        if(n=="tab")return MKey(Tabulator,mods);
+        if(n=="return")return MKey(Return,mods);
+        if(n=="esc")return MKey(Escape,mods);
+        if(n=="caps")return MKey(CapsLock,mods);
+        if(n=="backspace")return MKey(Backspace,mods);
+        if(n=="space")return MKey(Space,mods);
+        if(n=="num")return MKey(NumLock,mods);
+        //unknown sequence or no key
+        if(n=="nokey" || n=="error" || n=="none")return MKey();
+        qDebug()<<"Warning: unknown key type"<<n;
+        return MKey();
+}
+
+bool MKey::isPrintable(bool ign) const
+{
+        //ctrl-* and alt-* cannot be printed
+        if(!ign && (mmod&(Ctrl|Alt)))return false;
+        //exclude some special keys
+        switch(mtype){
+                case CharKey:return mchar.isPrint();
+                case Space:return true;
+                default:return false;
+        }
+}
+
+QChar MKey::toChar(bool ign) const
+{
+        if(!isPrintable(ign))return QChar();
+        if(mtype==Space)return ' ';
+        if(mtype==CharKey)return mchar;
+        //hmm?
+        return QChar();
+}
+
+
+QString MKeySequence::toString() const
+{
+        QString ret;
+        //go through sequence and convert
+        bool altmode=false;
+        int altseq=0;
+        for(MKey key:*this){
+//                 qDebug()<<"conv"<<key.toCharName();
+                //enable alt mode?
+                if(key.modifiers()==MKey::Alt){
+                        altmode=true;
+                        const int d=key.toChar(true).digitValue();
+                        if(d>=0){
+                                altseq*=10;
+                                altseq+=d;
+                        }
+//                         qDebug()<<"Alt: "<<d<<altseq<<key.toChar(true);
+                        continue;
+                }
+                if(altmode && !key.modifiers().testFlag(MKey::Alt)){
+                        altmode=false;
+                        if(altseq>0)
+                                ret+=QChar::fromLatin1(altseq);
+//                         qDebug()<<"Alt compl:"<<altseq<<QChar::fromLatin1(altseq);
+                        altseq=0;
+                        //fall through and process remainder
+                }
+                //printable?
+                if(key.isPrintable())ret+=key.toChar();
+        }
+        //residual alt mode?
+        if(altmode && altseq>0)ret+=QChar::fromLatin1(altseq);
+        //done
+        return ret;
+}
+
+void MHidKeyEvent::reduce()
+{
+        QByteArray r;
+        for(unsigned char c:mkeys)
+                if(c>1)r.append(c);
+        mkeys=r;
+}
+
+qint64 MHidKeySequence::msSinceLastKey() const
+{
+        if(isEmpty())return 0;
+        return at(size()-1).timestamp().msecsTo(QDateTime::currentDateTime());
+}
diff --git a/plugins/bcs-usb/bcskeyboard.h b/plugins/bcs-usb/bcskeyboard.h
new file mode 100644 (file)
index 0000000..b9e6d30
--- /dev/null
@@ -0,0 +1,193 @@
+//
+// C++ Interface: plugin for USB barcode scanners: Scanner Implementation
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_BCSUSB_KBD_H
+#define MAGICSMOKE_BCSUSB_KBD_H
+
+#include <QList>
+#include <QObject>
+#include <QFlag>
+#include <QDateTime>
+#include <QMap>
+
+class MKey
+{
+        public:
+                enum Modifier {
+                        None = 0,
+                        Ctrl = 1,
+                        Shift = 2,
+                        Alt = 4,
+                        AltGr = 8,
+                        GUI = 16,
+                };
+                Q_DECLARE_FLAGS(Modifiers,Modifier);
+                
+                enum KeyType{
+                        AllKeyRelease = 0,
+                        CharKey = 1,
+                        CapsLock = 2,
+                        NumLock = 3,
+                        Backspace = 4,
+                        Return = 5,
+                        Tabulator = 6,
+                        Space = 7,
+                        ModifierOnly =8,
+                        Escape = 9,
+                };
+                
+                MKey(){}
+                MKey(const MKey&k,Modifiers m);
+                MKey(const MKey&)=default;
+                MKey(MKey&&)=default;
+                
+                MKey(QChar,Modifiers m=None);
+                MKey(KeyType t,Modifiers m=None):mmod(m),mtype(t){}
+                
+                MKey& operator=(const MKey&)=default;
+                MKey& operator=(MKey&&)=default;
+                
+                bool operator==(const MKey&);
+                bool operator!=(const MKey&k){return !operator==(k);}
+                
+                bool isValid()const{return mtype!=AllKeyRelease;}
+                
+                Modifiers modifiers()const{return mmod;}
+                QChar toChar(bool ignoremodifier=false)const;
+                bool isPrintable(bool ignoremodifier=false)const;
+                
+                ///returns the encoded character name as used in layout files
+                QString toCharName()const;
+                ///returns a key object matching the encoded character name from a layout file
+                static MKey fromCharName(QString);
+                
+                MKey toLower()const{MKey r(*this);r.mchar=r.mchar.toLower();return r;}
+                MKey toUpper()const{MKey r(*this);r.mchar=r.mchar.toUpper();return r;}
+        private:
+                Modifiers mmod=None;
+                KeyType mtype=AllKeyRelease;
+                QChar mchar;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(MKey::Modifiers)
+
+class MKeySequence:public QList<MKey>
+{
+        public:
+                MKeySequence(){}
+                MKeySequence(const QList< MKey >& l):QList< MKey >(l){}
+                MKeySequence(const MKeySequence& other)=default;
+                MKeySequence(MKeySequence&&)=default;
+                
+                MKeySequence& operator=(const QList<MKey>&l){QList<MKey>::operator=(l);return *this;}
+                MKeySequence& operator=(const MKeySequence&)=default;
+                MKeySequence& operator=(MKeySequence&&)=default;
+                
+                QString toString()const;
+};
+
+class MHidKeyEvent
+{
+        inline static MKey::Modifiers modconv(unsigned char m){
+                MKey::Modifiers r=MKey::None;
+                if(m&0x11)r=MKey::Ctrl;
+                if(m&0x22)r|=MKey::Shift;
+                if(m&0x04)r|=MKey::Alt;
+                if(m&0x40)r|=MKey::AltGr;
+                if(m&0x88)r|=MKey::GUI;
+                return r;
+        }
+        public:
+                
+                MHidKeyEvent(){}
+                MHidKeyEvent(unsigned char mod,QByteArray keys):mmod(modconv(mod)),mkeys(keys){reduce();}
+                MHidKeyEvent(const MHidKeyEvent&)=default;
+                MHidKeyEvent(MHidKeyEvent&&)=default;
+                
+                MHidKeyEvent& operator=(const MHidKeyEvent&)=default;
+                MHidKeyEvent& operator=(MHidKeyEvent&&)=default;
+                
+                MKey::Modifiers modifiers()const{return mmod;}
+                QByteArray keycodes()const{return mkeys;}
+                QDateTime timestamp()const{return mtime;}
+                int lastkeycode()const{if(mkeys.size()>0)return mkeys[mkeys.size()-1];else return 0;}
+                
+        private:
+                QDateTime mtime=QDateTime::currentDateTime();
+                MKey::Modifiers mmod=MKey::None;
+                QByteArray mkeys;
+                void reduce();
+};
+
+class MHidKeySequence:public QList<MHidKeyEvent>
+{
+        public:
+                MHidKeySequence(){}
+                MHidKeySequence(QList< MHidKeyEvent >& other):QList< MHidKeyEvent >(other){}
+                MHidKeySequence(const MHidKeySequence&)=default;
+                MHidKeySequence(MHidKeySequence&&)=default;
+                
+                MHidKeySequence& operator=(const MHidKeySequence&)=default;
+                MHidKeySequence& operator=(MHidKeySequence&&)=default;
+                
+                qint64 msSinceLastKey()const;
+};
+
+class MHidKeyLayout
+{
+        public:
+                ///loads the base layout only
+                MHidKeyLayout();
+                ///loads the specified layout
+                explicit MHidKeyLayout(QString name);
+                MHidKeyLayout(const MHidKeyLayout&)=default;
+                MHidKeyLayout(MHidKeyLayout&&)=default;
+                
+                MHidKeyLayout& operator=(const MHidKeyLayout&)=default;
+                MHidKeyLayout& operator=(MHidKeyLayout&&)=default;
+                
+                MKeySequence toKeys(const MHidKeySequence&)const;
+                QString toString(const MHidKeySequence&)const;
+                
+                ///attempts to load a layout file, normally the name mentioned
+                ///should not contain a complete path or the suffix .kbl - the
+                ///implementation will automatically search for a matching file;
+                ///if it does contain a slash it is assumed to be a complete file name
+                bool loadLayout(QString filename);
+                bool parseLayout(QString content,QString filename=QString());
+                void resetLayout();
+                
+                QString toLayoutDoc()const;
+                
+                void setKeyMapping(unsigned char keycode,const QList<MKey> &c);
+                void clearKeyMapping();
+                
+                ///returns the human readable name of the layout
+                QString layoutName()const{return mlname;}
+                ///returns the iso code of the layout (base of the file name)
+                ///or an empty string if this is a custom layout
+                QString layoutCodeName()const;
+                ///returns the full file name of the layout
+                QString layoutFileName()const{return mfilename;}
+                
+                void setLayoutName(QString l){mlname=l;}
+                
+                ///returns true if this is a populated layout
+                bool isValid()const{return mkeymap.size()>0;}
+                
+                static QMap<QString,QString> findLayouts();
+        private:
+                QString mfilename,mlname;
+                QMap<unsigned char,QList<MKey>>mkeymap;
+};
+
+#endif
diff --git a/plugins/bcs-usb/configwidget.cpp b/plugins/bcs-usb/configwidget.cpp
new file mode 100644 (file)
index 0000000..4a6350d
--- /dev/null
@@ -0,0 +1,69 @@
+//
+// C++ Implementation: Barcode Plugin for USB: Config
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "configwidget.h"
+
+#include "barcode-plugin.h"
+#include <QFormLayout>
+#include <QComboBox>
+#include <QPushButton>
+
+const QString MUsbBarcodeConfig::settingsGroup="usbBarcodeScanner";
+const QString MUsbBarcodeConfig::intervalKey="interval";
+const QString MUsbBarcodeConfig::timeoutKey="timeoutms";
+const QString MUsbBarcodeConfig::terminatorKey="terminatorkey";
+const QString MUsbBarcodeConfig::layoutKey="kbdlayout";
+const QString MUsbBarcodeConfig::nameKey="name";
+const QString MUsbBarcodeConfig::activeKey="active";
+const QString MUsbBarcodeConfig::vendorKey="vendorid";
+const QString MUsbBarcodeConfig::productKey="productid";
+const QString MUsbBarcodeConfig::serialKey="serial";
+const QString MUsbBarcodeConfig::useSerialKey="useserial";
+const QString MUsbBarcodeConfig::interfaceKey="interfaceid";
+
+MUsbBarcodeConfig::MUsbBarcodeConfig(MBarcodeConfiguration* cfg)
+{
+        QFormLayout *fl;
+        setLayout(fl=new QFormLayout);
+        fl->addRow(tr("Detection Interval"),minterval=new QComboBox);
+        minterval->setEditable(false);
+        minterval->addItem(tr("Driver Inactive"),0);
+        minterval->addItem(tr("5 seconds"),5);
+        minterval->addItem(tr("10 seconds"),10);
+        minterval->addItem(tr("20 seconds"),20);
+        minterval->addItem(tr("30 seconds"),30);
+        QPushButton*p;
+        fl->addRow(tr("Add Scanner"),p=new QPushButton(tr("add...")));
+        connect(p,SIGNAL(clicked(bool)),this,SLOT(addScanner()));
+        
+        cfg->addTab(this,tr("USB Settings"));
+}
+
+void MUsbBarcodeConfig::saveConfig()
+{
+
+}
+
+void MUsbBarcodeConfig::addScanner()
+{
+
+}
+
+
+MUsbScannerConfig::MUsbScannerConfig(MBarcodeConfiguration* cfg, QString id)
+{
+        //TODO: use a better name for the tab
+        cfg->addTab(this,tr("USB Scanner: %1").arg(id));
+}
+
+void MUsbScannerConfig::saveConfig()
+{
+
+}
diff --git a/plugins/bcs-usb/configwidget.h b/plugins/bcs-usb/configwidget.h
new file mode 100644 (file)
index 0000000..b47483b
--- /dev/null
@@ -0,0 +1,55 @@
+//
+// C++ Interface: plugin for USB barcode scanners
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_BCSUSB_CONFIG_H
+#define MAGICSMOKE_BCSUSB_CONFIG_H
+
+#include <QWidget>
+
+class QComboBox;
+class MBarcodeConfiguration;
+class MUsbBarcodeConfig:public QWidget
+{
+        Q_OBJECT
+        public:
+                MUsbBarcodeConfig(MBarcodeConfiguration*);
+                
+                static const QString settingsGroup;
+                static const QString intervalKey;
+                static const QString timeoutKey;
+                static const QString terminatorKey;
+                static const QString layoutKey;
+                static const QString nameKey;
+                static const QString activeKey;
+                static const QString vendorKey;
+                static const QString productKey;
+                static const QString serialKey;
+                static const QString useSerialKey;
+                static const QString interfaceKey;
+
+        public slots:
+                void saveConfig();
+                void addScanner();
+        private:
+                QComboBox*minterval;
+};
+
+class MUsbScannerConfig:public QWidget
+{
+        Q_OBJECT
+        public:
+                MUsbScannerConfig(MBarcodeConfiguration*,QString);
+        public slots:
+                void saveConfig();
+};
+
+#endif
diff --git a/plugins/bcs-usb/hidscanner.cpp b/plugins/bcs-usb/hidscanner.cpp
new file mode 100644 (file)
index 0000000..78c5168
--- /dev/null
@@ -0,0 +1,113 @@
+//
+// C++ Implementation: Barcode Plugin for USB: Scanner
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "hidscanner.h"
+#include "configwidget.h"
+
+#include <QDebug>
+#include <QTimer>
+#include <QSettings>
+
+MHidBarcodeScanner::MHidBarcodeScanner(QString path,QString setGrp, QObject* parent)
+        : MBarcodeScanner(parent),mpath(path)
+{
+        qDebug()<<"Trying to open device at"<<path;
+        activate();
+        //read settings
+        QSettings set;
+        set.beginGroup(MUsbBarcodeConfig::settingsGroup);
+        set.beginGroup(setGrp);
+        mtimeout=set.value(MUsbBarcodeConfig::timeoutKey,0).toInt();
+        mterminator=MKey::fromCharName(set.value(MUsbBarcodeConfig::terminatorKey).toString());
+        mname=set.value(MUsbBarcodeConfig::nameKey,mpath).toString();
+        mlayout.loadLayout(set.value(MUsbBarcodeConfig::layoutKey,"qwerty").toString());
+}
+
+MHidBarcodeScanner::~MHidBarcodeScanner()
+{
+        deactivate();
+}
+
+void MHidBarcodeScanner::activate()
+{
+        if(mdev!=nullptr && mrdtmr!=nullptr)return;
+        mdev=hid_open_path(mpath.toLocal8Bit().data());
+        if(!mdev){
+                qDebug()<<"Unable to open device.";
+                deleteLater();
+                return;
+        }
+        hid_set_nonblocking(mdev,1);
+        mrdtmr=new QTimer(this);
+        mrdtmr->setSingleShot(false);
+        mrdtmr->start(20);
+        connect(mrdtmr,SIGNAL(timeout()),this,SLOT(readData()));
+        emit activated();
+}
+
+void MHidBarcodeScanner::deactivate()
+{
+        if(mrdtmr)delete mrdtmr;
+        mrdtmr=nullptr;
+        if(mdev)hid_close(mdev);
+        mdev=nullptr;
+        emit deactivated();
+}
+
+QString MHidBarcodeScanner::readableName() const
+{
+        return mname;
+}
+
+void MHidBarcodeScanner::readData()
+{
+        if(mdev==nullptr){
+                if(mseq.size()>0)processData();
+                return;
+        }
+        //read data
+        unsigned char buf[64];
+        memset(buf,0,sizeof(buf));
+        while(true){
+                const int rs=hid_read(mdev,buf,sizeof(buf));
+                if(rs<0){
+                        qDebug()<<"Error reading from USB device"<<mpath;
+                        deactivate();
+                        break;
+                }
+                if(rs==0)break;
+                if(rs<3){
+                        qDebug()<<"Warning: invalid report from USB device"<<mpath<<"expected >=3 bytes, got"<<rs;
+                }
+                mseq.append(MHidKeyEvent(buf[0],QByteArray((char*)buf+2,rs-2)));
+        }
+        processData();
+}
+
+void MHidBarcodeScanner::processData()
+{
+        if(mseq.isEmpty())return;
+        //is the timeout over?
+        bool force=false;
+        if(mtimeout>0 && mseq.msSinceLastKey()>=mtimeout)
+                force=true;
+        //can we see one of the termination codes?
+        if(!force && !mterminator.isValid())return;
+        MKeySequence kseq=mlayout.toKeys(mseq);
+        if(!force && !kseq.contains(mterminator))return;
+        //convert
+        QString bc=kseq.toString();
+        mseq.clear();
+        //remove superfluous chars
+        //verify checksum and remove it
+        //emit barcode
+        qDebug()<<"scanned barcode"<<bc;
+        emit newBarcode(bc);
+}
diff --git a/plugins/bcs-usb/hidscanner.h b/plugins/bcs-usb/hidscanner.h
new file mode 100644 (file)
index 0000000..44fa89a
--- /dev/null
@@ -0,0 +1,51 @@
+//
+// C++ Interface: plugin for USB barcode scanners: Scanner Implementation
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_BCSUSB_SCANNER_H
+#define MAGICSMOKE_BCSUSB_SCANNER_H
+
+#include <barcode-plugin.h>
+
+#include <hidapi.h>
+
+#include "bcskeyboard.h"
+
+class QTimer;
+class MHidBarcodeScanner:public MBarcodeScanner
+{
+        Q_OBJECT
+        public:
+                explicit MHidBarcodeScanner(QString path,QString setGrp,QObject* parent = 0);
+                virtual ~MHidBarcodeScanner();
+                
+                QString readableName()const override;
+                bool isActive()const override{return mdev!=nullptr;}
+                
+                void activate() override;
+                void deactivate() override;
+                
+                bool matchPath(const QString &p){return mpath==p;}
+                
+        private slots:
+                void readData();
+                void processData();
+        private:
+                hid_device*mdev=nullptr;
+                QTimer*mrdtmr=nullptr;
+                QString mpath,mname;
+                MHidKeySequence mseq;
+                MHidKeyLayout mlayout;
+                int mtimeout=0;
+                MKey mterminator;
+};
+
+#endif
diff --git a/plugins/bcs-usb/kbdlayouts/base.kbl b/plugins/bcs-usb/kbdlayouts/base.kbl
new file mode 100644 (file)
index 0000000..7d5fcac
--- /dev/null
@@ -0,0 +1,75 @@
+#basic invisible layout, used by other layouts
+0 nokey
+1 error
+
+#top row
+0x29 ESC
+0x3a F1
+0x3b F2
+0x3c F3
+0x3d F4
+0x3e F5
+0x3f F6
+0x40 F7
+0x41 F8
+0x41 F9
+0x43 F10
+0x44 F11
+0x45 F12
+
+0x46 print
+0x47 scroll
+0x48 pause
+
+
+#numeric row
+0x1e 1
+0x1f 2
+0x20 3
+0x21 4
+0x22 5
+0x23 6
+0x24 7
+0x25 8
+0x26 9
+0x27 0
+0x2a backspace
+
+#stable meta keys
+0x2b tab
+0x39 caps
+0x28 return return
+0x2c space space space
+#0x65 menu
+
+#directional blocks
+#0x49 ins
+#0x4c del
+#0x4a home
+#0x4d end
+#0x4b pgUp
+#0x4e pgDown
+
+#0x52 up
+#0x51 down
+#0x50 left
+#0x4f right
+
+#numeric keypad
+0x62 0
+0x63 .
+0x58 return
+0x59 1
+0x5a 2
+0x5b 3
+0x5c 4
+0x5d 5
+0x5e 6
+0x5f 7
+0x60 8
+0x61 9
+0x53 num
+0x54 /
+0x55 *
+0x56 minus
+0x57 plus
diff --git a/plugins/bcs-usb/kbdlayouts/de.kbl b/plugins/bcs-usb/kbdlayouts/de.kbl
new file mode 100644 (file)
index 0000000..74abd70
--- /dev/null
@@ -0,0 +1,35 @@
+title German / Deutsch
+
+include qwertz
+
+#numeric row
+0x35 ^ Â°
+0x1e 1 !
+0x1f 2 "
+0x20 3 Â§
+0x21 4 $
+0x22 5 %
+0x23 6 &
+0x24 7 / {
+0x25 8 ( [
+0x26 9 ) ]
+0x27 0 = }
+0x2d ÃŸ ? \
+0x2e ' `
+
+
+#special chars on alpha block
+0x2f Ã¼
+0x30 plus *
+0x31 # '
+
+0x33 Ã¶
+0x34 Ã¤
+
+0x36 , ;
+0x37 . :
+0x38 minus _
+0x64 < > |
+
+#numeric keypad
+0x63 ,
diff --git a/plugins/bcs-usb/kbdlayouts/qwerty.kbl b/plugins/bcs-usb/kbdlayouts/qwerty.kbl
new file mode 100644 (file)
index 0000000..c2676e0
--- /dev/null
@@ -0,0 +1,35 @@
+#basic invisible qwerty layout, used by other layouts
+
+include base
+
+#top row
+0x14 q
+0x1a w
+0x8 e
+0x15 r
+0x17 t
+0x1c y
+0x18 u
+0xc i
+0x12 o
+0x13 p
+
+#home row
+0x4 a
+0x16 s
+0x7 d
+0x9 f
+0xa g
+0xb h
+0xd j
+0xe k
+0xf l
+
+#bottom row
+0x1d z
+0x1b x
+0x6 c
+0x19 v
+0x5 b
+0x11 n
+0x10 m
diff --git a/plugins/bcs-usb/kbdlayouts/qwertz.kbl b/plugins/bcs-usb/kbdlayouts/qwertz.kbl
new file mode 100644 (file)
index 0000000..36639e7
--- /dev/null
@@ -0,0 +1,35 @@
+#basic invisible qwertz layout, used by other layouts
+
+include base
+
+#top row
+0x14 q
+0x1a w
+0x8 e
+0x15 r
+0x17 t
+0x1c z
+0x18 u
+0xc i
+0x12 o
+0x13 p
+
+#home row
+0x4 a
+0x16 s
+0x7 d
+0x9 f
+0xa g
+0xb h
+0xd j
+0xe k
+0xf l
+
+#bottom row
+0x1d y
+0x1b x
+0x6 c
+0x19 v
+0x5 b
+0x11 n
+0x10 m
diff --git a/plugins/bcs-usb/kbdlayouts/us.kbl b/plugins/bcs-usb/kbdlayouts/us.kbl
new file mode 100644 (file)
index 0000000..855821d
--- /dev/null
@@ -0,0 +1,31 @@
+title US American (Intl)
+
+include qwerty
+
+#numeric row
+0x35 ` ~
+0x1e 1 !
+0x1f 2 @
+0x20 3 #
+0x21 4 $
+0x22 5 %
+0x23 6 ^
+0x24 7 &
+0x25 8 *
+0x26 9 (
+0x27 0 )
+0x2d minus _
+0x2e = plus
+
+#special chars on alpha block
+0x2f [ {
+0x30 ] }
+0x31 \ |
+
+0x33 ; :
+0x34 ' "
+
+0x36 , <
+0x37 . >
+0x38 / ?
+
diff --git a/plugins/bcs-usb/layouts.qrc b/plugins/bcs-usb/layouts.qrc
new file mode 100644 (file)
index 0000000..2146333
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE RCC>
+<!-- Copyright: (c) Konrad Rosenbaum, 2014
+See COPYING.GPL for details. -->
+<RCC version="1.0">
+    <qresource>
+        <file>kbdlayouts/base.kbl</file>
+        <file>kbdlayouts/qwerty.kbl</file>
+        <file>kbdlayouts/qwertz.kbl</file>
+        <file>kbdlayouts/us.kbl</file>
+        <file>kbdlayouts/de.kbl</file>
+    </qresource>
+</RCC>
diff --git a/plugins/bcscanner/bcs-plugin.cpp b/plugins/bcscanner/bcs-plugin.cpp
deleted file mode 100644 (file)
index 5aac522..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// C++ Implementation: Barcode Plugin Basics
-//
-//
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013
-//
-// Copyright: See README/COPYING.GPL files that come with this distribution
-//
-//
-
-#include "bcs-plugin.h"
-
-MHidBarcodePlugin::~MHidBarcodePlugin()
-{
-
-}
-
-
-void MHidBarcodePlugin::configure(MBarcodeConfiguration* )
-{
-
-}
-
-QStringList MHidBarcodePlugin::findScanners()
-{
-        return QStringList();
-}
diff --git a/plugins/bcscanner/bcs-plugin.h b/plugins/bcscanner/bcs-plugin.h
deleted file mode 100644 (file)
index 9271e19..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// C++ Interface: plugin base for barcode scanners
-//
-// Description: 
-//
-//
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013
-//
-// Copyright: See README/COPYING.GPL files that come with this distribution
-//
-//
-
-#ifndef MAGICSMOKE_BARCODE_PLUGIN_HID_H
-#define MAGICSMOKE_BARCODE_PLUGIN_HID_H
-
-#include "barcode-plugin.h"
-
-class MHidBarcodePlugin:public QObject,public MBarcodePlugin
-{
-        Q_OBJECT
-        Q_PLUGIN_METADATA(IID MBarcodePlugin_IID)
-        Q_INTERFACES(MBarcodePlugin)
-        public:
-                virtual ~MHidBarcodePlugin();
-                virtual QStringList findScanners() override;
-                virtual void configure(MBarcodeConfiguration*)override;
-
-};
-
-#endif
diff --git a/plugins/bcscanner/bcscanner.pro b/plugins/bcscanner/bcscanner.pro
deleted file mode 100644 (file)
index 1abd988..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = lib
-TARGET = msp-bcscanner
-CONFIG += plugin
-
-include (../../basics.pri)
-include (../../src/smoke.pri)
-include (hidapi.pri)
-
-SOURCES += bcs-plugin.cpp
-HEADERS += bcs-plugin.h
index 0034d33..88a3553 100644 (file)
@@ -1,2 +1,2 @@
 TEMPLATE = subdirs
-SUBDIRS = bcscanner
\ No newline at end of file
+SUBDIRS = bcs-usb
\ No newline at end of file
index 5a3e34e..f27abd7 100644 (file)
@@ -4,7 +4,7 @@
 // Description: Main Program
 //
 //
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007-2013
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007-2014
 //
 // Copyright: See README/COPYING.GPL files that come with this distribution
 //
@@ -33,6 +33,7 @@
 
 #include "configdialog.h"
 #include "debug.h"
+#include "barcode-plugin.h"
 #include "hmac.h"
 #include "keygen.h"
 #include "main.h"
@@ -390,6 +391,8 @@ void MApplication::initialize()
         initHelpUrl();
         //init updater
         initUpdater();
+        //init plugins
+        MBarcodeHub::instance()->initialize();
 }
 
 void MApplication::initUpdater()
index df21c91..020a189 100644 (file)
@@ -4,7 +4,7 @@
 // Description: 
 //
 //
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007-2011
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007-2014
 //
 // Copyright: See README/COPYING.GPL files that come with this distribution
 //
index 5f6cc5d..2542a43 100644 (file)
@@ -2,7 +2,7 @@
 // C++ Implementation: Barcode Plugin Basics
 //
 //
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
 //
 // Copyright: See README/COPYING.GPL files that come with this distribution
 //
 
 #include "barcode-plugin.h"
 
+#include <QBoxLayout>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QPluginLoader>
+#include <QPointer>
+#include <QPushButton>
+#include <QTabWidget>
+
+static QList<QPointer<MBarcodeScanner> >allscanners;
+static QList<MBarcodePlugin* >allplugins;
+
 MBarcodeConfiguration::MBarcodeConfiguration(QWidget* parent): QDialog(parent)
 {
+        setWindowTitle(tr("Configure Barcode Plugins"));
+        
+        QVBoxLayout*vl;
+        QHBoxLayout*hl;
+        QPushButton*p;
+        setLayout(vl=new QVBoxLayout);
+        vl->addWidget(mtab=new QTabWidget,1);
+        vl->addLayout(hl=new QHBoxLayout);
+        hl->addStretch(1);
+        hl->addWidget(p=new QPushButton(tr("&Save")));
+        connect(p,SIGNAL(clicked(bool)),this,SLOT(accept()));
+        hl->addWidget(p=new QPushButton(tr("&Cancel")));
+        connect(p,SIGNAL(clicked(bool)),this,SLOT(reject()));
+        
+        setSizeGripEnabled(true);
+        
+        for(MBarcodePlugin*plug:allplugins)
+                if(plug)plug->configure(this);
+}
+
+void MBarcodeConfiguration::addTab(QWidget* w, QString t)
+{
+        mtab->addTab(w,t);
+}
+
+
+
+MBarcodeHub* MBarcodeHub::instance()
+{
+        static MBarcodeHub hub;
+        return &hub;
+}
 
+MBarcodeHub::MBarcodeHub(): QObject()
+{
+        qDebug()<<"instantiating barcode hub";
 }
 
-void MBarcodeConfiguration::addTab(QWidget* , QString )
+MBarcodeHub::~MBarcodeHub()
 {
+        qDebug()<<"deleting barcode hub";
+}
 
+MBarcodeScanner::MBarcodeScanner(QObject* parent): QObject(parent)
+{
+}
+
+void MBarcodePlugin::registerScanner(MBarcodeScanner* scanner)
+{
+        if(!scanner)return;
+        allscanners.append(scanner);
+        QObject::connect(scanner,SIGNAL(newBarcode(QString)), MBarcodeHub::instance(),SIGNAL(newBarcode(QString)));
+}
+
+MBarcodePlugin::~MBarcodePlugin()
+{}
+
+QList< MBarcodeScanner* > MBarcodeHub::allScanners()
+{
+        QList<MBarcodeScanner*>ret;
+        for(MBarcodeScanner*s:allscanners)
+                if(s)ret<<s;
+        //TODO: prune the allscanners list
+        return ret;
+}
+
+void MBarcodeHub::initialize()
+{
+        //get install dir
+        const QString dir=QCoreApplication::applicationDirPath();
+        //go through all potential barcode plugins (msp-bcs-*.so or ...*.dll)
+        for(const QString file:QDir(dir).entryList(QStringList()<<"msp-bcs-*",QDir::Files)){
+                //attempt to load it
+                qDebug()<<"Attempting to load potential barcode plugin"<<file;
+                QPluginLoader pl(dir+"/"+file);
+                const bool r=pl.load();
+                qDebug()<<"   ...."<<file<<(r?"successfully loaded.":"not a valid plugin.");
+                if(r){
+                        QObject*oplug=pl.instance();
+                        if(!oplug)continue;
+                        MBarcodePlugin*plug=qobject_cast< MBarcodePlugin* >(oplug);
+                        if(!plug)continue;
+                        allplugins<<plug;
+                        connect(oplug,SIGNAL(destroyed(QObject*)), this,SLOT(removePlugin(QObject*)));
+                }
+        }
+}
+
+void MBarcodeHub::removePlugin(QObject* obj)
+{
+        if(!obj)return;
+        allplugins.removeAll(qobject_cast< MBarcodePlugin* >(obj));
 }
index 3f55249..dbeb6b3 100644 (file)
@@ -4,7 +4,7 @@
 // Description: 
 //
 //
-// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2013-2014
 //
 // Copyright: See README/COPYING.GPL files that come with this distribution
 //
 #include <QtPlugin>
 #include <QStringList>
 #include <QDialog>
+#include <QList>
 
 class QTabWidget;
 
+///Scanner Configuration Dialog
 class MAGICSMOKE_EXPORT MBarcodeConfiguration:public QDialog
 {
         Q_OBJECT
         public:
+                ///instantiates a new config dialog and asks plugins to display their tabs
                 MBarcodeConfiguration(QWidget*parent);
                 
         public slots:
+                ///used by plugins to add configuration tabs,
+                ///tabs should connect to the accepted() and rejected() signals to store data
                 void addTab(QWidget*,QString);
         private:
                 QTabWidget*mtab;
 };
 
+///base class of actual scanner implementations
+class MAGICSMOKE_EXPORT MBarcodeScanner:public QObject
+{
+        Q_OBJECT
+        public:
+                explicit MBarcodeScanner(QObject* parent = 0);
+                
+                ///returns a human readable name for the scanner
+                virtual QString readableName()const=0;
+                
+                ///returns whether the scanner is currently active
+                virtual bool isActive()const=0;
+                
+        public slots:
+                ///attempts to activate the scanner
+                virtual void activate()=0;
+                ///attempts to de-activate the scanner
+                virtual void deactivate()=0;
+        signals:
+                ///emitted when the scanner becomes active
+                void activated();
+                ///emitted when the scanner becomes inactive
+                void deactivated();
+                ///emitted when a new barcode is available
+                void newBarcode(QString);
+};
+
+///base class of barcode scanner plugins
 class MAGICSMOKE_EXPORT MBarcodePlugin
 {
         public:
-                virtual ~MBarcodePlugin(){}
+                virtual ~MBarcodePlugin();
 
-                virtual QStringList findScanners()=0;
+                ///called whenever the user opens a configuration dialog
                 virtual void configure(MBarcodeConfiguration*)=0;
+        protected:
+                ///called by plugins to register a new scanner that has come online
+                static void registerScanner(MBarcodeScanner*);
+};
+
+///central barcode scanner hub, this is used by widgets that require barcodes,
+///plugins report (indirectly) to the hub
+class MAGICSMOKE_EXPORT MBarcodeHub:public QObject
+{
+        Q_OBJECT
+        public:
+                ///returns the hub object
+                static MBarcodeHub*instance();
+                
+                ///initializes plugins (unless already initialized)
+                void initialize();
+                
+                ///returns all currently active scanners
+                QList<MBarcodeScanner*>allScanners();
+                
+        signals:
+                ///this is emitted whenever any of the barcode scanners find a new barcode
+                void newBarcode(QString);
+                
+        private slots:
+                ///automatically called by when a plugin destructs
+                void removePlugin(QObject*);
+        private:
+                MBarcodeHub();
+                virtual ~MBarcodeHub();
 };
 
 #define MBarcodePlugin_IID "de.silmor.MagicSmoke.BarcodePlugin/1.0"
index 229639f..565a3d5 100644 (file)
@@ -2,13 +2,15 @@ HEADERS += \
        $$PWD/debug.h \
        $$PWD/waitcursor.h \
        $$PWD/sclock.h \
-       $$PWD/dommodel.h
+       $$PWD/dommodel.h \
+       $$PWD/barcode-plugin.h
 
 SOURCES += \
        $$PWD/code39.cpp \
        $$PWD/debug.cpp \
        $$PWD/waitcursor.cpp \
        $$PWD/sclock.cpp \
-       $$PWD/dommodel.cpp
+       $$PWD/dommodel.cpp \
+       $$PWD/barcode-plugin.cpp
 
 INCLUDEPATH += $$PWD
index 3e4dd74..1feb769 100644 (file)
@@ -14,6 +14,7 @@
 #include "misc.h"
 #include "msinterface.h"
 #include "orderwin.h"
+#include "barcodeline.h"
 
 #include "entrancetab.h"
 
@@ -45,7 +46,7 @@ MEntranceTab::MEntranceTab(QString pk)
        entranceevent->setEditable(false);
        vl->addSpacing(30);
        vl->addWidget(new QLabel(tr("Enter or scan Ticket-ID:")),0);
-       vl->addWidget(entrancescan=new QLineEdit,0);
+       vl->addWidget(entrancescan=new MBarcodeLine,0);
        connect(entrancescan,SIGNAL(returnPressed()),this,SLOT(entranceValidate()));
        vl->addWidget(entrancelabel=new QLabel("  "),10);
        entrancelabel->setAutoFillBackground(true);
index 114ef24..426a89e 100644 (file)
@@ -32,6 +32,8 @@
 
 #include "centbox.h"
 #include "sclock.h"
+#include "barcode-plugin.h"
+#include "labeldlg.h"
 
 #include "jsengine.h"
 
@@ -57,8 +59,6 @@
 #include <QFormLayout>
 #include <QDateTimeEdit>
 
-#include <labeldlg.h>
-
 #include "MTChangeMyPassword"
 #include "MTReturnTicketVoucher"
 #include "MTDeductVoucher"
@@ -144,6 +144,7 @@ MOverview::MOverview(QString pk,std::function<void(int,QString)>initUpdate)
        m2->addAction(tr("&Display settings..."),this,SLOT(displaySettings()));
        m2->addAction(tr("&Label Printing settings..."),this,SLOT(labelSettings()));
         m2->addAction(tr("&OpenOffice settings..."),this,SLOT(openOfficeSettings()));
+        m2->addAction(tr("&Barcode Scanner settings..."),this,SLOT(barcodeSettings()));
 
        //make sure webrequest knows its settings
        webSettings(false);
@@ -631,6 +632,12 @@ void MOverview::openOfficeSettings()
         c.exec();
 }
 
+void MOverview::barcodeSettings()
+{
+        MBarcodeConfiguration bc(this);
+        bc.exec();
+}
+
 void MOverview::aclWindow()
 {
        MAclWindow::showWindow(this);
index 380c955..d6d4b63 100644 (file)
@@ -77,6 +77,8 @@ class MOverview:public MTabWin
                void labelSettings();
                 ///OpenOffice settings
                 void openOfficeSettings();
+                ///barcode scanner settings
+                void barcodeSettings();
                
                ///\internal run init scripts
                void runStartupScript();
diff --git a/src/widgets/barcodeline.cpp b/src/widgets/barcodeline.cpp
new file mode 100644 (file)
index 0000000..16a50bb
--- /dev/null
@@ -0,0 +1,36 @@
+//
+// C++ Implementation: barcode line edit
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+
+#include "barcodeline.h"
+#include "barcode-plugin.h"
+
+MBarcodeLine::MBarcodeLine(QWidget* parent): MBarcodeLine(QString(),parent)
+{
+}
+
+MBarcodeLine::MBarcodeLine(const QString& cont, QWidget* parent): QLineEdit(cont,parent)
+{
+        setToolTip(tr("Type a barcode into this line or scan it with a barcode scanner."));
+        setPlaceholderText(tr("Type or scan a barcode."));
+        connect(MBarcodeHub::instance(),SIGNAL(newBarcode(QString)), this,SLOT(setBarcode(QString)));
+}
+
+void MBarcodeLine::setBarcode(QString bc)
+{
+        //do we actually have focus?
+        //TODO: maybe this should be visibility?
+        if(!hasFocus())return;
+        //enter barcode and emit ready signal
+        setText(bc);
+        emit returnPressed();
+}
diff --git a/src/widgets/barcodeline.h b/src/widgets/barcodeline.h
new file mode 100644 (file)
index 0000000..6da7b37
--- /dev/null
@@ -0,0 +1,28 @@
+//
+// C++ Interface: barcode line edit
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2014
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_BARCODELINE_H
+#define MAGICSMOKE_BARCODELINE_H
+
+#include <QLineEdit>
+
+class MBarcodeLine:public QLineEdit
+{
+        Q_OBJECT
+        public:
+                explicit MBarcodeLine(QWidget* parent = 0);
+                explicit MBarcodeLine(const QString& , QWidget* parent = 0);
+        private slots:
+                void setBarcode(QString);
+};
+
+#endif
index f758a46..ac92607 100644 (file)
@@ -1,11 +1,13 @@
 HEADERS += \
        widgets/centbox.h \
        widgets/listview.h \
-       widgets/treeview.h
+       widgets/treeview.h \
+       widgets/barcodeline.h
 
 SOURCES += \
        widgets/centbox.cpp \
        widgets/listview.cpp \
-       widgets/treeview.cpp
+       widgets/treeview.cpp \
+       widgets/barcodeline.cpp
 
 INCLUDEPATH += ./widgets