Initial commit.
authorJimmy Christensen <dusted@dusted.dk>
Wed, 18 Jun 2014 12:22:07 +0000 (14:22 +0200)
committerJimmy Christensen <dusted@dusted.dk>
Wed, 18 Jun 2014 12:22:07 +0000 (14:22 +0200)
.classpath [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.project [new file with mode: 0644]
lib/jssc-2.8.0-javadoc.jar [new file with mode: 0644]
lib/jssc-2.8.0.jar [new file with mode: 0644]
src/fkgui/ConsoleMsg.java [new file with mode: 0644]
src/fkgui/FireActionListener.java [new file with mode: 0644]
src/fkgui/MainWin.java [new file with mode: 0644]
src/fkgui/SerialWorker.java [new file with mode: 0644]
src/fkgui/finalkey.png [new file with mode: 0644]
src/org/eclipse/wb/swt/SWTResourceManager.java [new file with mode: 0644]

diff --git a/.classpath b/.classpath
new file mode 100644 (file)
index 0000000..0925262
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.osgi_3.9.1.v20140110-1610.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.osgi.source_3.9.1.v20140110-1610.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.core.commands_3.6.100.v20130515-1857.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.core.commands.source_3.6.100.v20130515-1857.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.equinox.common_3.6.200.v20130402-1505.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.equinox.common.source_3.6.200.v20130402-1505.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.equinox.registry_3.5.301.v20130717-1549.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.equinox.registry.source_3.5.301.v20130717-1549.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.core.runtime_3.9.100.v20131218-1515.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.core.runtime.source_3.9.100.v20131218-1515.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.text_3.5.300.v20130515-1451.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.text.source_3.5.300.v20130515-1451.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.swt.gtk.linux.x86_64_3.102.1.v20140206-1358.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.swt.gtk.linux.x86_64.source_3.102.1.v20140206-1358.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.jface_3.9.1.v20130725-1141.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.jface.source_3.9.1.v20130725-1141.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.jface.text_3.8.101.v20130802-1147.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.jface.text.source_3.8.101.v20130802-1147.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.ui.workbench_3.105.2.v20140211-1711.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.ui.workbench.source_3.105.2.v20140211-1711.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/com.ibm.icu_50.1.1.v201304230130.jar" sourcepath="/usr/share/eclipse/plugins/com.ibm.icu.source_50.1.1.v201304230130.jar"/>
+       <classpathentry kind="lib" path="/usr/share/eclipse/plugins/org.eclipse.ui.forms_3.6.1.v20130822-1117.jar" sourcepath="/usr/share/eclipse/plugins/org.eclipse.ui.forms.source_3.6.1.v20130822-1117.jar"/>
+       <classpathentry kind="lib" path="lib/jssc-2.8.0-javadoc.jar"/>
+       <classpathentry kind="lib" path="lib/jssc-2.8.0.jar">
+               <attributes>
+                       <attribute name="javadoc_location" value="jar:platform:/resource/FinalKeyGUI/lib/jssc-2.8.0-javadoc.jar!/"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..eff77c5
--- /dev/null
@@ -0,0 +1,2 @@
+./bin/
+./settings/
diff --git a/.project b/.project
new file mode 100644 (file)
index 0000000..9adc245
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>FinalKeyGUI</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/lib/jssc-2.8.0-javadoc.jar b/lib/jssc-2.8.0-javadoc.jar
new file mode 100644 (file)
index 0000000..75efc8b
Binary files /dev/null and b/lib/jssc-2.8.0-javadoc.jar differ
diff --git a/lib/jssc-2.8.0.jar b/lib/jssc-2.8.0.jar
new file mode 100644 (file)
index 0000000..d2b5c07
Binary files /dev/null and b/lib/jssc-2.8.0.jar differ
diff --git a/src/fkgui/ConsoleMsg.java b/src/fkgui/ConsoleMsg.java
new file mode 100644 (file)
index 0000000..c481c8f
--- /dev/null
@@ -0,0 +1,8 @@
+package fkgui;
+
+import java.awt.PopupMenu;
+
+public interface ConsoleMsg {
+       public void log(String msg);
+       public PopupMenu getPopup();
+}
diff --git a/src/fkgui/FireActionListener.java b/src/fkgui/FireActionListener.java
new file mode 100644 (file)
index 0000000..7c717d9
--- /dev/null
@@ -0,0 +1,48 @@
+package fkgui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import jssc.SerialPort;
+import jssc.SerialPortException;
+
+public class FireActionListener implements ActionListener {
+       public String name;
+       public String num;
+       public String action;
+       public SerialPort port;
+       public void actionPerformed(ActionEvent e) {
+               System.out.println("Performing action " + action + " on account ("+num+") - "+name );
+               if(!action.equals("%"))
+               {
+                       System.out.println("Here");
+                       try
+                       {
+                               port.writeBytes(action.getBytes());
+                       } catch( Exception ex )
+                       {
+                               ex.printStackTrace();
+                       }
+               }
+               
+               try {
+                       port.writeBytes(num.toLowerCase().getBytes());
+       
+                       try {
+                               Thread.sleep(400);
+                       } catch (InterruptedException e1) {
+                               // TODO Auto-generated catch block
+                               e1.printStackTrace();
+                       }
+                       System.out.println( port.readString() );
+                       
+               } catch (SerialPortException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               }
+               
+               
+               
+       }
+
+}
diff --git a/src/fkgui/MainWin.java b/src/fkgui/MainWin.java
new file mode 100644 (file)
index 0000000..791f0ac
--- /dev/null
@@ -0,0 +1,330 @@
+package fkgui;
+
+
+import  java.util.prefs.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.wb.swt.SWTResourceManager;
+
+import fkgui.SerialWorker.SerialState;
+
+public class MainWin implements PropertyChangeListener, ConsoleMsg {
+
+       protected Shell shell;
+       private Text txtPsw;
+       public TrayIcon trayIcon;
+       public PopupMenu popup;
+       public MenuItem showMain;
+       public MenuItem hideMain;
+       public Text txtLog;
+       public Button btnStart;
+       private Text txtDev;
+       Preferences prefs;
+       
+       SerialWorker fkSerial;
+       private boolean sysTrayIconVisible;
+
+       
+       static final String PORT_PREF ="lastUsedPortPref";
+       static final String DEFAULT_DEVICE = "/dev/FinalKey";
+       
+       /**
+        * Launch the application.
+        * @param args
+        */
+       public static void main(String[] args) {
+
+               try {
+                       MainWin window = new MainWin();
+                       window.open();
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+               
+       }
+
+       /**
+        * Open the window.
+        */
+       public void open() {
+               Display display = Display.getDefault();
+               createContents();
+               shell.open();
+               shell.layout();
+               
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch()) {
+                               display.sleep();                                
+                       }
+               }
+       }
+       
+       public void log( String str )
+       {
+               txtLog.append(str+"\n");
+               txtLog.redraw();
+               shell.redraw();
+               System.out.println(str);
+       }
+
+       /**
+        * Create a systemTray icon
+        */
+       private void createSysTrayIcon()
+       {
+               sysTrayIconVisible=true;
+        //Check the SystemTray is supported
+        if (!SystemTray.isSupported()) {
+            log("SystemTray is not supported, app is useless");
+            return;
+        }
+        
+        try {
+                       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+               } catch (ClassNotFoundException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               } catch (InstantiationException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               } catch (IllegalAccessException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               } catch (UnsupportedLookAndFeelException e1) {
+                       // TODO Auto-generated catch block
+                       e1.printStackTrace();
+               }
+
+        popup = new PopupMenu();
+        trayIcon =
+                new TrayIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("finalkey.png")));
+        trayIcon.setImageAutoSize(true);
+        final SystemTray tray = SystemTray.getSystemTray();
+       
+        // Create a pop-up menu components
+        showMain = new MenuItem("Show FinalKey");
+        hideMain = new MenuItem("Hide FinalKey");
+
+        showMain.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               Display.getDefault().syncExec( new Runnable(){
+                                       
+                                       public void run()
+                                       {
+                               //Remove myself and make main window visible
+                                               showFromTray();
+                                       }
+                               } );
+                       }
+                               
+               });
+        
+        hideMain.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               Display.getDefault().syncExec( new Runnable(){
+                                       
+                                       public void run()
+                                       {
+                                               hideToTray();
+                                       }
+                               } );
+                       }
+                               
+               });
+        
+
+
+        
+        //Add components to pop-up menu
+        popup.add(hideMain);
+        popup.addSeparator();
+
+
+        trayIcon.setPopupMenu(popup);
+       
+        try {
+            tray.add(trayIcon);
+        } catch (AWTException e) {
+               log("TrayIcon could not be added.");
+        }      
+        
+       }
+       
+       private void hideToTray()
+       {
+               popup.remove(0);
+               popup.insert(showMain, 0);
+               shell.setVisible(false);
+       }
+       
+       private void showFromTray()
+       {
+               popup.remove(0);
+               popup.insert(hideMain, 0);
+               shell.setVisible(true);
+               shell.forceActive();
+       }
+       
+       private void destroySysTrayIcon()
+       {
+               sysTrayIconVisible=false;
+               SystemTray.getSystemTray().remove(trayIcon);
+       }
+       
+       public void shutDownApp()
+       {
+               if(fkSerial != null)
+               {
+                       fkSerial.disconnect();
+               }
+               if(sysTrayIconVisible == true)
+               {
+                       destroySysTrayIcon();
+               }
+               System.exit(0);
+       }
+       
+       /**
+        * Create contents of the window.
+        */
+       protected void createContents() {
+               shell = new Shell();
+               shell.setImage(SWTResourceManager.getImage(MainWin.class, "/fkgui/finalkey.png"));
+               shell.setSize(450, 495);
+               shell.setText("Final Key (Not connected)");
+               
+               shell.setLayout(null);
+
+               
+               prefs = Preferences.userNodeForPackage(this.getClass());
+               
+
+               fkSerial = new SerialWorker(this);
+               fkSerial.addPropertyChangeListener(this);
+               
+
+               btnStart = new Button(shell, SWT.NONE);
+               btnStart.setBounds(330, 10, 104, 52);
+               btnStart.addSelectionListener(new SelectionAdapter() {
+                       public void widgetSelected(SelectionEvent e) {
+                               
+                               if( fkSerial.state == SerialState.Connected )
+                               {
+                                       shell.setText("Final Key (Not connected)");
+                                       fkSerial.disconnect();
+                                       txtPsw.setVisible(true);
+                                       txtDev.setVisible(true);
+                                       
+                                       btnStart.setText("Connect");
+                               } else {
+                                       prefs.put(PORT_PREF, txtDev.getText() );
+                                       fkSerial.connect(txtDev.getText(),txtPsw.getText());
+                                       //Eat the password for security reasons.
+                                       txtPsw.setText("");
+                                       txtPsw.setVisible(false);
+                                       txtDev.setVisible(false);
+                                       btnStart.setText("Disconnect");
+                                       shell.setText("Final Key (Connected)");
+                                       
+                               }
+                       }
+               });
+               btnStart.setText("Connect");
+               Label lblPassword = new Label(shell, SWT.NONE);
+               lblPassword.setText("Password");
+               lblPassword.setBounds(10, 39, 85, 23);
+               
+               txtPsw = new Text(shell, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+               txtPsw.setBounds(101, 39, 223, 23);
+               
+               txtLog = new Text(shell, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI);
+               txtLog.setEditable(false);
+               txtLog.setBounds(10, 68, 424, 388);
+               
+               Label lblPort = new Label(shell, SWT.NONE);
+               lblPort.setBounds(10, 10, 76, 23);
+               lblPort.setText("Port");
+               
+               txtDev = new Text(shell, SWT.BORDER);
+               txtDev.setFont(SWTResourceManager.getFont("Cantarell", 9, SWT.NORMAL));
+               txtDev.setText( prefs.get(PORT_PREF, DEFAULT_DEVICE));
+               txtDev.setBounds(101, 10, 223, 23);
+               shell.setTabList(new Control[]{txtPsw, btnStart});
+               
+               log("Welcome!\nConnect your Final Key and enter password.\nThen press connect.\nPress the button when it blinks.\n----------\n");
+
+
+               createSysTrayIcon();
+               
+               
+               
+               shell.addShellListener( new ShellListener() {
+                       
+                       public void shellIconified(ShellEvent e) {
+
+                                       
+
+                       }
+                       
+                       public void shellDeiconified(ShellEvent e) {
+                               // TODO Auto-generated method stub
+                               
+                       }
+                       
+                       public void shellDeactivated(ShellEvent e) {
+                               // TODO Auto-generated method stub
+                               
+                       }
+                       
+                       public void shellClosed(ShellEvent e) {
+                               shutDownApp();
+                               
+                       }
+                       
+                       public void shellActivated(ShellEvent e) {
+                               // TODO Auto-generated method stub
+                               
+                       }
+               } );
+               
+
+
+       }
+
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               
+               if( evt.getPropertyName().equals("serialState") )
+               {
+                       System.out.println( "serialState changed from "+((SerialState)evt.getOldValue())+" to "+((SerialState)evt.getNewValue()) );
+               }
+               
+               
+       }
+
+       @Override
+       public PopupMenu getPopup() {
+               return this.popup;
+       }
+}
diff --git a/src/fkgui/SerialWorker.java b/src/fkgui/SerialWorker.java
new file mode 100644 (file)
index 0000000..93027d5
--- /dev/null
@@ -0,0 +1,287 @@
+package fkgui;
+
+import java.awt.Menu;
+import java.awt.MenuItem;
+import java.util.List;
+
+import jssc.SerialPort;
+import jssc.SerialPortEvent;
+import jssc.SerialPortEventListener;
+import jssc.SerialPortException;
+
+import org.eclipse.swt.widgets.Display;
+
+
+public class SerialWorker extends javax.swing.SwingWorker<Void, String> implements SerialPortEventListener {
+       public String dev;
+       public String pass;
+       private SerialPort serialPort;
+       SerialState state;
+       private ConsoleMsg delegate; 
+       
+       public enum SerialState { Connecting, Connected, Disconnected };
+
+
+       public SerialWorker(ConsoleMsg d) {
+               delegate=d;
+               state = SerialState.Disconnected;
+       }
+       
+       public void connect(String d, String p)
+       {
+               dev=d;
+               pass=p;
+               
+               serialPort = new SerialPort(dev);               
+               firePropertyChange("serialState", state, SerialState.Connecting );
+               state = SerialState.Connecting;
+               
+               execute();
+       }
+       
+       public void disconnect()
+       {
+               firePropertyChange("serialState", state, SerialState.Disconnected );
+
+               
+               if(serialPort != null && serialPort.isOpened() )
+               {
+                       try {
+                               serialPort.closePort();
+                       } catch (SerialPortException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public String expectString(String expect, int timeOut)
+       {
+               
+               //Read from port, and if not found within 2 seconds, exit with null
+               String in=new String();
+               int msLeft=timeOut;
+               while(true)
+               {
+                       try {
+                               if( serialPort.getInputBufferBytesCount() > 0)
+                               {
+                                       in += serialPort.readString(serialPort.getInputBufferBytesCount());
+                                       
+                                       if( in.contains(expect))
+                                       {
+                                               return(in);
+                                       }
+                               } else {
+                                       Thread.sleep(10);
+                                       if(timeOut>0)
+                                       {
+                                               msLeft -=10;
+                                               if( msLeft < 1)
+                                               {
+                                                       break;
+                                               }
+                                       }
+                               }
+                       } catch (Exception e)
+                       {
+                               //I don't care
+                       }
+               }
+               
+               return( null );
+       }
+       
+       
+       @Override
+       protected Void doInBackground() throws Exception {
+               publish("Trying to connect to "+dev);
+               /**
+                * Connection strategy:
+                * Open the port, wait for "The Final Key" followed by # on next line, (getLoginHeader)
+                *      * If not coming, press q and try once more.
+                * When [Granted] record [Keyboard: and query full list with Xk
+                * 
+                */
+               
+               int numAccounts=0;
+               try {
+                       System.out.println("Port opened: " + serialPort.openPort());
+                       System.out.println("Params setted: " + serialPort.setParams(9600, 8, 1, 0));
+                       
+                       serialPort.addEventListener(this);
+                       String test = expectString("The Final Key", 1000);
+                       if( test != null )
+                       {
+                               publish("Ready to log in, press button now.");
+                               publish("Waiting for button press...");
+                       } else {
+                               //Try logging out.
+                               serialPort.writeByte( (byte)'q');
+                               publish("State error, try again.");
+                               disconnect();
+                               return null;
+                       }
+                       
+                       if( expectString( "Pass:", 0 ) != null )
+                       {
+                               publish("Logging in...");
+                       } else {
+                               publish("Error: Did not get password prompt. Unplug and try again.");
+                               disconnect();
+                               return null;
+                       }
+
+                       serialPort.writeBytes(pass.getBytes());
+                       serialPort.writeByte( (byte)13 );
+                       pass = "";
+                       
+                       
+                       if( expectString( "[Granted]", 200 ) != null )
+                       {
+                               publish("Access Granted.");
+                       } else {
+                               publish("Error: Access Denied.");
+                               disconnect();
+                               return null;
+                       }
+
+                       publish("Getting account list...");
+                       serialPort.writeByte( (byte)'X'); //Machine commands with uppercase X
+                       expectString("[auto]", 200);
+                       serialPort.writeByte( (byte)'l'); //Full list 
+
+                       
+                       String accounts = new String();
+                       
+                       int timeOut = 10000;
+                       while(true)
+                       {
+                               
+                               if( serialPort.getInputBufferBytesCount() > 0 )
+                               {
+                                       accounts += serialPort.readString();
+                                       String sub = accounts.substring( accounts.length()-3 );
+                                       if( sub.equals("\r\n>") )
+                                       {
+                                               accounts = accounts.substring( 0, accounts.length()-3 );
+                                               break;
+                                       }
+                               } else {
+                                       Thread.sleep(10);
+                                       timeOut-=10;
+                                       if(timeOut < 1)
+                                       {
+                                               publish("Error getting account list.");
+                                               disconnect();
+                                               return null;
+                                       }
+                               }
+                       }
+                       
+                       //Trim first 3
+                       accounts = accounts.substring(3);
+
+                       String[] lines = accounts.split( "\r\n" );
+                       numAccounts=lines.length;
+                       for(String l:lines)
+                       {
+                               String ac = l.substring(0,2);
+                               String an = l.substring(2);
+                               
+                               //publish( "Account number: "+ac+" ["+an+"]");
+
+                               Menu menu = new Menu(an+" ["+ac+"]");
+                               MenuItem both = new MenuItem("User + Pass");
+                               MenuItem usr = new MenuItem("User");
+                               MenuItem psw = new MenuItem("Pass");
+                               menu.add(both);
+                               menu.add(usr);
+                               menu.add(psw);
+
+                               FireActionListener fal = new FireActionListener();
+                               fal.action = "%";
+                               fal.name = an;
+                               fal.num = ac;
+                               fal.port = serialPort;
+                               both.addActionListener(fal);
+
+
+                               fal = new FireActionListener();
+                               fal.action = "p";
+                               fal.name = an;
+                               fal.num = ac;
+                               fal.port = serialPort;
+                               psw.addActionListener(fal);                     
+
+                               fal = new FireActionListener();
+                               fal.action = "u";
+                               fal.name = an;
+                               fal.num = ac;
+                               fal.port = serialPort;
+                               usr.addActionListener(fal);                     
+
+                               delegate.getPopup().add(menu);
+                       }
+
+               }
+               catch (SerialPortException ex){
+                       System.out.println(ex);
+               } catch (Exception e )
+               {
+                       System.out.println("Other exception: "+e.getMessage() );
+                       e.printStackTrace();
+               }
+
+               if(numAccounts==1)
+               {
+                       publish(numAccounts+" account.");
+               } else {
+                       publish(numAccounts+" accounts ready.");
+               }
+
+               publish("Use the systray icon to trigger.");
+
+               firePropertyChange("serialState", state, SerialState.Connected );
+               state = SerialState.Connected;
+               
+               return null;
+       }
+
+
+       private class MainWinMsg implements Runnable {
+               private String msg;
+               private ConsoleMsg delegate;
+               MainWinMsg(ConsoleMsg d, String m)
+               {
+                       delegate=d;
+                       msg=m;
+               }
+               public void run() {
+                       delegate.log(msg);
+               }
+       }
+       
+       @Override
+       protected void process(List<String> msgs) {
+               for(String s : msgs)
+               {
+                       
+                       Display.getDefault().asyncExec( new MainWinMsg(delegate, s) );
+               }
+       }
+
+       @Override
+       public void serialEvent(SerialPortEvent event) {
+               
+               if(event.isBREAK())
+                       System.out.println(">>BREAK);");
+               if(event.isERR())
+                       System.out.println(">>Err");
+               if(serialPort == null)
+                       System.out.println(">>Null");
+
+       }
+
+
+}
diff --git a/src/fkgui/finalkey.png b/src/fkgui/finalkey.png
new file mode 100644 (file)
index 0000000..0665e90
Binary files /dev/null and b/src/fkgui/finalkey.png differ
diff --git a/src/org/eclipse/wb/swt/SWTResourceManager.java b/src/org/eclipse/wb/swt/SWTResourceManager.java
new file mode 100644 (file)
index 0000000..8b6d4cc
--- /dev/null
@@ -0,0 +1,447 @@
+/*******************************************************************************\r
+ * Copyright (c) 2011 Google, Inc.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *    Google, Inc. - initial API and implementation\r
+ *******************************************************************************/\r
+package org.eclipse.wb.swt;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
+ * <p>
+ * !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the
+ * operating system resources managed by cached objects when those objects and OS resources are no longer
+ * needed (e.g. on application shutdown)
+ * <p>
+ * This class may be freely distributed as part of any application or plugin.
+ * <p>
+ * @author scheglov_ke
+ * @author Dan Rubel
+ */
+public class SWTResourceManager {
+       ////////////////////////////////////////////////////////////////////////////
+       //
+       // Color
+       //
+       ////////////////////////////////////////////////////////////////////////////
+       private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>();
+       /**
+        * Returns the system {@link Color} matching the specific ID.
+        * 
+        * @param systemColorID
+        *            the ID value for the color
+        * @return the system {@link Color} matching the specific ID
+        */
+       public static Color getColor(int systemColorID) {
+               Display display = Display.getCurrent();
+               return display.getSystemColor(systemColorID);
+       }
+       /**
+        * Returns a {@link Color} given its red, green and blue component values.
+        * 
+        * @param r
+        *            the red component of the color
+        * @param g
+        *            the green component of the color
+        * @param b
+        *            the blue component of the color
+        * @return the {@link Color} matching the given red, green and blue component values
+        */
+       public static Color getColor(int r, int g, int b) {
+               return getColor(new RGB(r, g, b));
+       }
+       /**
+        * Returns a {@link Color} given its RGB value.
+        * 
+        * @param rgb
+        *            the {@link RGB} value of the color
+        * @return the {@link Color} matching the RGB value
+        */
+       public static Color getColor(RGB rgb) {
+               Color color = m_colorMap.get(rgb);
+               if (color == null) {
+                       Display display = Display.getCurrent();
+                       color = new Color(display, rgb);
+                       m_colorMap.put(rgb, color);
+               }
+               return color;
+       }
+       /**
+        * Dispose of all the cached {@link Color}'s.
+        */
+       public static void disposeColors() {
+               for (Color color : m_colorMap.values()) {
+                       color.dispose();
+               }
+               m_colorMap.clear();
+       }
+       ////////////////////////////////////////////////////////////////////////////
+       //
+       // Image
+       //
+       ////////////////////////////////////////////////////////////////////////////
+       /**
+        * Maps image paths to images.
+        */
+       private static Map<String, Image> m_imageMap = new HashMap<String, Image>();
+       /**
+        * Returns an {@link Image} encoded by the specified {@link InputStream}.
+        * 
+        * @param stream
+        *            the {@link InputStream} encoding the image data
+        * @return the {@link Image} encoded by the specified input stream
+        */
+       protected static Image getImage(InputStream stream) throws IOException {
+               try {
+                       Display display = Display.getCurrent();
+                       ImageData data = new ImageData(stream);
+                       if (data.transparentPixel > 0) {
+                               return new Image(display, data, data.getTransparencyMask());
+                       }
+                       return new Image(display, data);
+               } finally {
+                       stream.close();
+               }
+       }
+       /**
+        * Returns an {@link Image} stored in the file at the specified path.
+        * 
+        * @param path
+        *            the path to the image file
+        * @return the {@link Image} stored in the file at the specified path
+        */
+       public static Image getImage(String path) {
+               Image image = m_imageMap.get(path);
+               if (image == null) {
+                       try {
+                               image = getImage(new FileInputStream(path));
+                               m_imageMap.put(path, image);
+                       } catch (Exception e) {
+                               image = getMissingImage();
+                               m_imageMap.put(path, image);
+                       }
+               }
+               return image;
+       }
+       /**
+        * Returns an {@link Image} stored in the file at the specified path relative to the specified class.
+        * 
+        * @param clazz
+        *            the {@link Class} relative to which to find the image
+        * @param path
+        *            the path to the image file, if starts with <code>'/'</code>
+        * @return the {@link Image} stored in the file at the specified path
+        */
+       public static Image getImage(Class<?> clazz, String path) {
+               String key = clazz.getName() + '|' + path;
+               Image image = m_imageMap.get(key);
+               if (image == null) {
+                       try {
+                               image = getImage(clazz.getResourceAsStream(path));
+                               m_imageMap.put(key, image);
+                       } catch (Exception e) {
+                               image = getMissingImage();
+                               m_imageMap.put(key, image);
+                       }
+               }
+               return image;
+       }
+       private static final int MISSING_IMAGE_SIZE = 10;
+       /**
+        * @return the small {@link Image} that can be used as placeholder for missing image.
+        */
+       private static Image getMissingImage() {
+               Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+               //
+               GC gc = new GC(image);
+               gc.setBackground(getColor(SWT.COLOR_RED));
+               gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+               gc.dispose();
+               //
+               return image;
+       }
+       /**
+        * Style constant for placing decorator image in top left corner of base image.
+        */
+       public static final int TOP_LEFT = 1;
+       /**
+        * Style constant for placing decorator image in top right corner of base image.
+        */
+       public static final int TOP_RIGHT = 2;
+       /**
+        * Style constant for placing decorator image in bottom left corner of base image.
+        */
+       public static final int BOTTOM_LEFT = 3;
+       /**
+        * Style constant for placing decorator image in bottom right corner of base image.
+        */
+       public static final int BOTTOM_RIGHT = 4;
+       /**
+        * Internal value.
+        */
+       protected static final int LAST_CORNER_KEY = 5;
+       /**
+        * Maps images to decorated images.
+        */
+       @SuppressWarnings("unchecked")
+       private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+       /**
+        * Returns an {@link Image} composed of a base image decorated by another image.
+        * 
+        * @param baseImage
+        *            the base {@link Image} that should be decorated
+        * @param decorator
+        *            the {@link Image} to decorate the base image
+        * @return {@link Image} The resulting decorated image
+        */
+       public static Image decorateImage(Image baseImage, Image decorator) {
+               return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+       }
+       /**
+        * Returns an {@link Image} composed of a base image decorated by another image.
+        * 
+        * @param baseImage
+        *            the base {@link Image} that should be decorated
+        * @param decorator
+        *            the {@link Image} to decorate the base image
+        * @param corner
+        *            the corner to place decorator image
+        * @return the resulting decorated {@link Image}
+        */
+       public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+               if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+                       throw new IllegalArgumentException("Wrong decorate corner");
+               }
+               Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+               if (cornerDecoratedImageMap == null) {
+                       cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
+                       m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+               }
+               Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
+               if (decoratedMap == null) {
+                       decoratedMap = new HashMap<Image, Image>();
+                       cornerDecoratedImageMap.put(baseImage, decoratedMap);
+               }
+               //
+               Image result = decoratedMap.get(decorator);
+               if (result == null) {
+                       Rectangle bib = baseImage.getBounds();
+                       Rectangle dib = decorator.getBounds();
+                       //
+                       result = new Image(Display.getCurrent(), bib.width, bib.height);
+                       //
+                       GC gc = new GC(result);
+                       gc.drawImage(baseImage, 0, 0);
+                       if (corner == TOP_LEFT) {
+                               gc.drawImage(decorator, 0, 0);
+                       } else if (corner == TOP_RIGHT) {
+                               gc.drawImage(decorator, bib.width - dib.width, 0);
+                       } else if (corner == BOTTOM_LEFT) {
+                               gc.drawImage(decorator, 0, bib.height - dib.height);
+                       } else if (corner == BOTTOM_RIGHT) {
+                               gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height);
+                       }
+                       gc.dispose();
+                       //
+                       decoratedMap.put(decorator, result);
+               }
+               return result;
+       }
+       /**
+        * Dispose all of the cached {@link Image}'s.
+        */
+       public static void disposeImages() {
+               // dispose loaded images
+               {
+                       for (Image image : m_imageMap.values()) {
+                               image.dispose();
+                       }
+                       m_imageMap.clear();
+               }
+               // dispose decorated images
+               for (int i = 0; i < m_decoratedImageMap.length; i++) {
+                       Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];
+                       if (cornerDecoratedImageMap != null) {
+                               for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) {
+                                       for (Image image : decoratedMap.values()) {
+                                               image.dispose();
+                                       }
+                                       decoratedMap.clear();
+                               }
+                               cornerDecoratedImageMap.clear();
+                       }
+               }
+       }
+       ////////////////////////////////////////////////////////////////////////////
+       //
+       // Font
+       //
+       ////////////////////////////////////////////////////////////////////////////
+       /**
+        * Maps font names to fonts.
+        */
+       private static Map<String, Font> m_fontMap = new HashMap<String, Font>();
+       /**
+        * Maps fonts to their bold versions.
+        */
+       private static Map<Font, Font> m_fontToBoldFontMap = new HashMap<Font, Font>();
+       /**
+        * Returns a {@link Font} based on its name, height and style.
+        * 
+        * @param name
+        *            the name of the font
+        * @param height
+        *            the height of the font
+        * @param style
+        *            the style of the font
+        * @return {@link Font} The font matching the name, height and style
+        */
+       public static Font getFont(String name, int height, int style) {
+               return getFont(name, height, style, false, false);
+       }
+       /**
+        * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
+        * flags are also supported.
+        * 
+        * @param name
+        *            the name of the font
+        * @param size
+        *            the size of the font
+        * @param style
+        *            the style of the font
+        * @param strikeout
+        *            the strikeout flag (warning: Windows only)
+        * @param underline
+        *            the underline flag (warning: Windows only)
+        * @return {@link Font} The font matching the name, height, style, strikeout and underline
+        */
+       public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) {
+               String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline;
+               Font font = m_fontMap.get(fontName);
+               if (font == null) {
+                       FontData fontData = new FontData(name, size, style);
+                       if (strikeout || underline) {
+                               try {
+                                       Class<?> logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$
+                                       Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$
+                                       if (logFont != null && logFontClass != null) {
+                                               if (strikeout) {
+                                                       logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+                                               }
+                                               if (underline) {
+                                                       logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+                                               }
+                                       }
+                               } catch (Throwable e) {
+                                       System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$
+                               }
+                       }
+                       font = new Font(Display.getCurrent(), fontData);
+                       m_fontMap.put(fontName, font);
+               }
+               return font;
+       }
+       /**
+        * Returns a bold version of the given {@link Font}.
+        * 
+        * @param baseFont
+        *            the {@link Font} for which a bold version is desired
+        * @return the bold version of the given {@link Font}
+        */
+       public static Font getBoldFont(Font baseFont) {
+               Font font = m_fontToBoldFontMap.get(baseFont);
+               if (font == null) {
+                       FontData fontDatas[] = baseFont.getFontData();
+                       FontData data = fontDatas[0];
+                       font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD);
+                       m_fontToBoldFontMap.put(baseFont, font);
+               }
+               return font;
+       }
+       /**
+        * Dispose all of the cached {@link Font}'s.
+        */
+       public static void disposeFonts() {
+               // clear fonts
+               for (Font font : m_fontMap.values()) {
+                       font.dispose();
+               }
+               m_fontMap.clear();
+               // clear bold fonts
+               for (Font font : m_fontToBoldFontMap.values()) {
+                       font.dispose();
+               }
+               m_fontToBoldFontMap.clear();
+       }
+       ////////////////////////////////////////////////////////////////////////////
+       //
+       // Cursor
+       //
+       ////////////////////////////////////////////////////////////////////////////
+       /**
+        * Maps IDs to cursors.
+        */
+       private static Map<Integer, Cursor> m_idToCursorMap = new HashMap<Integer, Cursor>();
+       /**
+        * Returns the system cursor matching the specific ID.
+        * 
+        * @param id
+        *            int The ID value for the cursor
+        * @return Cursor The system cursor matching the specific ID
+        */
+       public static Cursor getCursor(int id) {
+               Integer key = Integer.valueOf(id);
+               Cursor cursor = m_idToCursorMap.get(key);
+               if (cursor == null) {
+                       cursor = new Cursor(Display.getDefault(), id);
+                       m_idToCursorMap.put(key, cursor);
+               }
+               return cursor;
+       }
+       /**
+        * Dispose all of the cached cursors.
+        */
+       public static void disposeCursors() {
+               for (Cursor cursor : m_idToCursorMap.values()) {
+                       cursor.dispose();
+               }
+               m_idToCursorMap.clear();
+       }
+       ////////////////////////////////////////////////////////////////////////////
+       //
+       // General
+       //
+       ////////////////////////////////////////////////////////////////////////////
+       /**
+        * Dispose of cached objects and their underlying OS resources. This should only be called when the cached
+        * objects are no longer needed (e.g. on application shutdown).
+        */
+       public static void dispose() {
+               disposeColors();
+               disposeImages();
+               disposeFonts();
+               disposeCursors();
+       }
+}
\ No newline at end of file