diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6a44e89
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+build/
+target/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Eclipse project files
+.classpath
+.project
+.settings
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# OSX
+.DS_Store
+
+# Gradle
+.gradle
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..b383510
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,191 @@
+
+ Copyright (c) 2009-2013, adakoda
+ Copyright (c) 2009-2013, Poly's Factory
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/bluecove-2.1.2.jar b/libs/bluecove-2.1.2.jar
new file mode 100644
index 0000000..8b8d504
Binary files /dev/null and b/libs/bluecove-2.1.2.jar differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..008af3a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,120 @@
+
+
+
+ 4.0.0
+
+ com.polysfactory.myglazz
+ myglazz-awt
+ 0.1.0-SNAPSHOT
+
+ MyGlazz
+ https://github.com/thorikawa/MyGlazz
+
+
+ UTF-8
+ UTF-8
+
+
+
+ https://github.com/thorikawa/MyGlazz
+ scm:git:git://github.com/thorikawa/MyGlazz.git
+ scm:git:ssh://git@github.com/thorikawa/MyGlazz.git
+
+
+
+ GitHub Issues
+ http://github.com/thorikawa/MyGlazz/issues
+
+
+
+
+ Apache 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+
+ Poly's Factory
+ http://polysfactory.com
+
+
+
+
+ net.sf.bluecove
+ bluecove
+ 2.1.2
+ system
+ ${project.basedir}/libs/bluecove-2.1.2.jar
+
+
+ com.polysfactory.lib.glass.bluetooth
+ glass-bluetooth
+ 0.1.0
+
+
+
+
+
+
+
+ org.sonatype.plugins
+ jarjar-maven-plugin
+ 1.7
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.0
+
+
+ 1.6
+
+
+
+
+
+
+
+ org.sonatype.plugins
+ jarjar-maven-plugin
+ 1.7
+
+
+ package
+
+ jarjar
+
+
+
+ net.sf.bluecove:bluecove
+ com.polysfactory.lib.glass.bluetooth:glass-bluetooth
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ true
+ com.polysfactory.myglazz.awt.MyGlazz
+
+
+
+
+
+
+
diff --git a/src/main/java/com/polysfactory/myglazz/awt/AboutDialog.java b/src/main/java/com/polysfactory/myglazz/awt/AboutDialog.java
new file mode 100644
index 0000000..8667bff
--- /dev/null
+++ b/src/main/java/com/polysfactory/myglazz/awt/AboutDialog.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009-2013 adakoda
+ * Copyright (C) 2009-2013 Poly's Factory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polysfactory.myglazz.awt;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.border.EmptyBorder;
+
+@SuppressWarnings("serial")
+public class AboutDialog extends JDialog {
+
+ public AboutDialog(Frame owner, boolean modal) {
+ super(owner, modal);
+
+ // Frame
+ setTitle("About MyGlazz");
+ setBounds(0, 0, 320, 140);
+ setResizable(false);
+
+ // Label
+ JLabel labelApp = new JLabel("MyGlazz Version 1.0.0");
+ JLabel labelCopyright = new JLabel("Copyright (C) 2013 Poly's Factory All rights reserved.");
+ JTextField labelUrl = new JTextField("http://polysfactory.com/");
+ labelUrl.setEditable(false);
+ labelUrl.setBorder(new EmptyBorder(0, 0, 0, 0));
+ labelUrl.addMouseListener(new MouseListener() {
+ public void mouseReleased(MouseEvent arg0) {
+ }
+
+ public void mousePressed(MouseEvent arg0) {
+ }
+
+ public void mouseExited(MouseEvent arg0) {
+ }
+
+ public void mouseEntered(MouseEvent arg0) {
+
+ }
+
+ public void mouseClicked(MouseEvent arg0) {
+ JTextField textField = (JTextField) arg0.getSource();
+ textField.selectAll();
+ }
+ });
+
+ // OK
+ JButton buttonOK = new JButton("OK");
+ buttonOK.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ onOK();
+ }
+ });
+
+ // Container
+ Container container1 = new Container();
+ FlowLayout flowLayout = new FlowLayout(FlowLayout.CENTER, 5, 5);
+ container1.setLayout(flowLayout);
+ container1.add(labelApp);
+ container1.add(labelCopyright);
+ container1.add(labelUrl);
+ container1.add(buttonOK);
+
+ Container containger = getContentPane();
+ containger.add(container1, BorderLayout.CENTER);
+ containger.add(buttonOK, BorderLayout.SOUTH);
+
+ // Key
+ {
+ AbstractAction actionOK = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ onOK();
+ }
+ };
+ AbstractAction actionCancel = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ onCancel();
+ }
+ };
+
+ JComponent targetComponent = getRootPane();
+ InputMap inputMap = targetComponent.getInputMap();
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "OK");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
+ targetComponent.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
+ targetComponent.getActionMap().put("OK", actionOK);
+ targetComponent.getActionMap().put("Cancel", actionCancel);
+ }
+ }
+
+ private void onOK() {
+ dispose();
+ }
+
+ private void onCancel() {
+ dispose();
+ }
+}
diff --git a/src/main/java/com/polysfactory/myglazz/awt/GlassConnection.java b/src/main/java/com/polysfactory/myglazz/awt/GlassConnection.java
new file mode 100644
index 0000000..dcabfef
--- /dev/null
+++ b/src/main/java/com/polysfactory/myglazz/awt/GlassConnection.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2013 Poly's Factory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polysfactory.myglazz.awt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.util.TimeZone;
+
+import javax.bluetooth.BluetoothStateException;
+import javax.bluetooth.DeviceClass;
+import javax.bluetooth.DiscoveryAgent;
+import javax.bluetooth.DiscoveryListener;
+import javax.bluetooth.LocalDevice;
+import javax.bluetooth.RemoteDevice;
+import javax.bluetooth.ServiceRecord;
+import javax.bluetooth.UUID;
+import javax.microedition.io.Connector;
+import javax.microedition.io.StreamConnection;
+
+import com.google.glass.companion.CompanionMessagingUtil;
+import com.google.glass.companion.GlassProtocol;
+import com.google.glass.companion.Proto.Envelope;
+import com.google.glass.companion.Proto.ScreenShot;
+
+public class GlassConnection {
+
+ private static final String SECURE_UUID = "F15CC914E4BC45CE9930CB7695385850";
+ private DiscoveryAgent discoveryAgent;
+ private String connectionURL;
+ private StreamConnection streamConnection;
+ private OutputStream outStream;
+ private InputStream inStream;
+ private GlassConnectionListener listener;
+ private GlassReaderThread mGlassReaderThread;
+
+ public GlassConnection() {
+ LocalDevice localDevice;
+ try {
+ localDevice = LocalDevice.getLocalDevice();
+ discoveryAgent = localDevice.getDiscoveryAgent();
+ } catch (BluetoothStateException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public RemoteDevice[] getBondedDevices() {
+ return discoveryAgent.retrieveDevices(DiscoveryAgent.PREKNOWN);
+ }
+
+ public void connect(RemoteDevice device) {
+ UUID[] uuidSet = new UUID[1];
+ uuidSet[0] = new UUID(SECURE_UUID, false);
+ try {
+ discoveryAgent.searchServices(null, uuidSet, device, new Client());
+ } catch (BluetoothStateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public void close() {
+ if (mGlassReaderThread != null) {
+ mGlassReaderThread.interrupt();
+ try {
+ mGlassReaderThread.join(10000);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ }
+ if (streamConnection != null) {
+ try {
+ streamConnection.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void setListener(GlassConnectionListener listener) {
+ this.listener = listener;
+ }
+
+ private class Client implements DiscoveryListener {
+
+ @Override
+ public void deviceDiscovered(RemoteDevice arg0, DeviceClass arg1) {
+ // TODO Auto-generated method stub
+ System.out.println("deviceDiscovered");
+ }
+
+ @Override
+ public void inquiryCompleted(int arg0) {
+ // TODO Auto-generated method stub
+ System.out.println("inquiryCompleted");
+ }
+
+ @Override
+ public void serviceSearchCompleted(int transID, int respCode) {
+ // TODO Auto-generated method stub
+ System.out.println("serviceSearchCompleted");
+ }
+
+ @Override
+ public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
+ connectionURL = null;
+ if (servRecord != null && servRecord.length > 0) {
+ ServiceRecord service = servRecord[0];
+ connectionURL = service.getConnectionURL(ServiceRecord.AUTHENTICATE_NOENCRYPT, false);
+ System.out.println(connectionURL);
+ }
+
+ try {
+ streamConnection = (StreamConnection) Connector.open(connectionURL);
+ outStream = streamConnection.openOutputStream();
+ inStream = streamConnection.openInputStream();
+ mGlassReaderThread = new GlassReaderThread();
+ mGlassReaderThread.start();
+
+ Envelope envelope = CompanionMessagingUtil.newEnvelope();
+ envelope.timezoneC2G = TimeZone.getDefault().getID();
+ write(envelope);
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Envelope envelope2 = CompanionMessagingUtil.newEnvelope();
+ ScreenShot screenShot = new ScreenShot();
+ screenShot.startScreenshotRequestC2G = true;
+ envelope2.screenshot = screenShot;
+ write(envelope2);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void write(Envelope envelope) {
+ try {
+ GlassProtocol.writeMessage(envelope, outStream);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public interface GlassConnectionListener {
+ public abstract void onReceivedEnvelope(Envelope envelope);
+ }
+
+ private class GlassReaderThread extends Thread {
+ public void run() {
+ try {
+ while (!Thread.currentThread().isInterrupted()) {
+ try {
+ Envelope envelope = (Envelope) GlassProtocol.readMessage(new Envelope(), inStream);
+ if (listener != null && envelope != null) {
+ listener.onReceivedEnvelope(envelope);
+ }
+ } catch (InterruptedIOException ie) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ System.out.println("Reader thread finished.");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ };
+ };
+
+}
diff --git a/src/main/java/com/polysfactory/myglazz/awt/MainFrame.java b/src/main/java/com/polysfactory/myglazz/awt/MainFrame.java
new file mode 100644
index 0000000..be78399
--- /dev/null
+++ b/src/main/java/com/polysfactory/myglazz/awt/MainFrame.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2009-2013 adakoda
+ * Copyright (C) 2013 Poly's Factory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polysfactory.myglazz.awt;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.prefs.Preferences;
+
+import javax.bluetooth.BluetoothStateException;
+import javax.bluetooth.RemoteDevice;
+import javax.imageio.ImageIO;
+import javax.swing.AbstractAction;
+import javax.swing.ButtonGroup;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.filechooser.FileFilter;
+
+import com.google.glass.companion.Proto.Envelope;
+import com.polysfactory.myglazz.awt.GlassConnection.GlassConnectionListener;
+
+@SuppressWarnings("serial")
+public class MainFrame extends JFrame implements GlassConnectionListener {
+
+ private static final String TITLE = "MyGlazz";
+
+ private static final int DEFAULT_WIDTH = 640;
+ private static final int DEFAULT_HEIGHT = 360;
+
+ private static final String EXT_PNG = "png";
+
+ private MainPanel mPanel;
+ private JPopupMenu mPopupMenu;
+
+ private Preferences mPrefs;
+ private int mRawImageWidth = DEFAULT_WIDTH;
+ private int mRawImageHeight = DEFAULT_HEIGHT;
+ private boolean mPortrait = true;
+ private double mZoom = 1.0;
+
+ private RemoteDevice mDevice;
+
+ private GlassConnection mGlassConnection;
+
+ public MainFrame(String[] args) {
+ initialize(args);
+ mGlassConnection = new GlassConnection();
+ }
+
+ public void selectDevice() {
+ mGlassConnection.setListener(null);
+ mGlassConnection.close();
+
+ RemoteDevice[] mDevices = null;
+ try {
+ mDevices = mGlassConnection.getBondedDevices();
+
+ if (mDevices != null) {
+ ArrayList list = new ArrayList();
+ for (int i = 0; i < mDevices.length; i++) {
+ String name = mDevices[i].getFriendlyName(false) + ":" + mDevices[i].getBluetoothAddress();
+ list.add(name);
+ }
+ SelectDeviceDialog dialog = new SelectDeviceDialog(this, true, list);
+ dialog.setLocationRelativeTo(this);
+ dialog.setVisible(true);
+ if (dialog.isOK()) {
+ int selectedIndex = dialog.getSelectedIndex();
+ if (selectedIndex >= 0) {
+ mDevice = mDevices[selectedIndex];
+ mGlassConnection.setListener(this);
+ mGlassConnection.connect(mDevice);
+ setImage(null);
+ }
+ }
+ }
+ } catch (BluetoothStateException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void setOrientation(boolean portrait) {
+ if (mPortrait != portrait) {
+ mPortrait = portrait;
+ savePrefs();
+ updateSize();
+ }
+ }
+
+ public void setZoom(double zoom) {
+ if (mZoom != zoom) {
+ mZoom = zoom;
+ savePrefs();
+ updateSize();
+ }
+ }
+
+ public void saveImage() {
+ BufferedImage inImage = mPanel.getImage();
+ if (inImage != null) {
+ BufferedImage outImage = new BufferedImage((int) (inImage.getWidth() * mZoom),
+ (int) (inImage.getHeight() * mZoom), inImage.getType());
+ if (outImage != null) {
+ AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(mZoom, mZoom),
+ AffineTransformOp.TYPE_BILINEAR);
+ op.filter(inImage, outImage);
+ JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setFileFilter(new FileFilter() {
+ @Override
+ public String getDescription() {
+ return "*." + EXT_PNG;
+ }
+
+ @Override
+ public boolean accept(File f) {
+ String ext = f.getName().toLowerCase();
+ return (ext.endsWith("." + EXT_PNG));
+ }
+ });
+ if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
+ try {
+ File file = fileChooser.getSelectedFile();
+ String path = file.getAbsolutePath();
+ if (!path.endsWith("." + EXT_PNG)) {
+ file = new File(path + "." + EXT_PNG);
+ }
+ ImageIO.write(outImage, EXT_PNG, file);
+ } catch (Exception ex) {
+ JOptionPane.showMessageDialog(this, "Failed to save a image.", "Save Image",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+ }
+
+ public void about() {
+ AboutDialog dialog = new AboutDialog(this, true);
+ dialog.setLocationRelativeTo(this);
+ dialog.setVisible(true);
+ }
+
+ public void updateSize() {
+ int width;
+ int height;
+ if (mPortrait) {
+ width = mRawImageWidth;
+ height = mRawImageHeight;
+ } else {
+ width = mRawImageHeight;
+ height = mRawImageWidth;
+ }
+ Insets insets = getInsets();
+ int newWidth = (int) (width * mZoom) + insets.left + insets.right;
+ int newHeight = (int) (height * mZoom) + insets.top + insets.bottom;
+
+ // Known bug
+ // If new window size is over physical window size, cannot update window
+ // size...
+ // FIXME
+ if ((getWidth() != newWidth) || (getHeight() != newHeight)) {
+ setSize(newWidth, newHeight);
+ }
+ }
+
+ public void setImage(BufferedImage fbImage) {
+ if (fbImage != null) {
+ mRawImageWidth = fbImage.getWidth();
+ mRawImageHeight = fbImage.getHeight();
+ }
+ mPanel.setFBImage(fbImage);
+ updateSize();
+ }
+
+ private void initialize(String[] args) {
+
+ initializePrefs();
+ initializeFrame();
+ initializePanel();
+ initializeMenu();
+ initializeActionMap();
+
+ addMouseListener(mMouseListener);
+ addWindowListener(mWindowListener);
+
+ pack();
+ setImage(null);
+ }
+
+ private void savePrefs() {
+ if (mPrefs != null) {
+ mPrefs.putInt("PrefVer", 1);
+ mPrefs.putBoolean("Portrait", mPortrait);
+ mPrefs.putDouble("Zoom", mZoom);
+ }
+ }
+
+ private void initializePrefs() {
+ mPrefs = Preferences.userNodeForPackage(this.getClass());
+ if (mPrefs != null) {
+ int prefVer = mPrefs.getInt("PrefVer", 1);
+ if (prefVer == 1) {
+ mPortrait = mPrefs.getBoolean("Portrait", true);
+ mZoom = mPrefs.getDouble("Zoom", 1.0);
+ }
+ }
+ }
+
+ private void initializeFrame() {
+ setTitle(TITLE);
+ // setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("icon.png")));
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setResizable(false);
+ }
+
+ private void initializePanel() {
+ mPanel = new MainPanel();
+ add(mPanel);
+ }
+
+ private void initializeMenu() {
+ mPopupMenu = new JPopupMenu();
+
+ initializeSelectDeviceMenu();
+ mPopupMenu.addSeparator();
+ initializeOrientationMenu();
+ initializeZoomMenu();
+ mPopupMenu.addSeparator();
+ initializeSaveImageMenu();
+ mPopupMenu.addSeparator();
+ initializeAbout();
+
+ mPopupMenu.addPopupMenuListener(new PopupMenuListener() {
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ }
+
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+ }
+
+ public void popupMenuCanceled(PopupMenuEvent e) {
+ }
+ });
+ }
+
+ private void initializeSelectDeviceMenu() {
+ JMenuItem menuItemSelectDevice = new JMenuItem("Select Device...");
+ menuItemSelectDevice.setMnemonic(KeyEvent.VK_D);
+ menuItemSelectDevice.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ selectDevice();
+ }
+ });
+ mPopupMenu.add(menuItemSelectDevice);
+ }
+
+ private void initializeOrientationMenu() {
+ JMenu menuOrientation = new JMenu("Orientation");
+ menuOrientation.setMnemonic(KeyEvent.VK_O);
+ mPopupMenu.add(menuOrientation);
+
+ ButtonGroup buttonGroup = new ButtonGroup();
+
+ // Portrait
+ JRadioButtonMenuItem radioButtonMenuItemPortrait = new JRadioButtonMenuItem("Portrait");
+ radioButtonMenuItemPortrait.setMnemonic(KeyEvent.VK_P);
+ radioButtonMenuItemPortrait.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ setOrientation(true);
+ }
+ });
+ if (mPortrait) {
+ radioButtonMenuItemPortrait.setSelected(true);
+ }
+ buttonGroup.add(radioButtonMenuItemPortrait);
+ menuOrientation.add(radioButtonMenuItemPortrait);
+
+ // Landscape
+ JRadioButtonMenuItem radioButtonMenuItemLandscape = new JRadioButtonMenuItem("Landscape");
+ radioButtonMenuItemLandscape.setMnemonic(KeyEvent.VK_L);
+ radioButtonMenuItemLandscape.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ setOrientation(false);
+ }
+ });
+ if (!mPortrait) {
+ radioButtonMenuItemLandscape.setSelected(true);
+ }
+ buttonGroup.add(radioButtonMenuItemLandscape);
+ menuOrientation.add(radioButtonMenuItemLandscape);
+ }
+
+ private void initializeZoomMenu() {
+ JMenu menuZoom = new JMenu("Zoom");
+ menuZoom.setMnemonic(KeyEvent.VK_Z);
+ mPopupMenu.add(menuZoom);
+
+ ButtonGroup buttonGroup = new ButtonGroup();
+
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.1, "10%", -1, mZoom);
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.25, "25%", -1, mZoom);
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.5, "50%", KeyEvent.VK_5, mZoom);
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 0.75, "75%", KeyEvent.VK_7, mZoom);
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 1.0, "100%", KeyEvent.VK_1, mZoom);
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 1.5, "150%", KeyEvent.VK_0, mZoom);
+ addRadioButtonMenuItemZoom(menuZoom, buttonGroup, 2.0, "200%", KeyEvent.VK_2, mZoom);
+ }
+
+ private void addRadioButtonMenuItemZoom(JMenu menuZoom, ButtonGroup buttonGroup, final double zoom, String caption,
+ int nemonic, double currentZoom) {
+ JRadioButtonMenuItem radioButtonMenuItemZoom = new JRadioButtonMenuItem(caption);
+ if (nemonic != -1) {
+ radioButtonMenuItemZoom.setMnemonic(nemonic);
+ }
+ radioButtonMenuItemZoom.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ setZoom(zoom);
+ }
+ });
+ if (currentZoom == zoom) {
+ radioButtonMenuItemZoom.setSelected(true);
+ }
+ buttonGroup.add(radioButtonMenuItemZoom);
+ menuZoom.add(radioButtonMenuItemZoom);
+ }
+
+ private void initializeSaveImageMenu() {
+ JMenuItem menuItemSaveImage = new JMenuItem("Save Image...");
+ menuItemSaveImage.setMnemonic(KeyEvent.VK_S);
+ menuItemSaveImage.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ saveImage();
+ }
+ });
+ mPopupMenu.add(menuItemSaveImage);
+ }
+
+ private void initializeActionMap() {
+ AbstractAction actionSelectDevice = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ selectDevice();
+ }
+ };
+ AbstractAction actionPortrait = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setOrientation(true);
+ }
+ };
+ AbstractAction actionLandscape = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setOrientation(false);
+ }
+ };
+ AbstractAction actionZoom50 = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setZoom(0.5);
+ }
+ };
+ AbstractAction actionZoom75 = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setZoom(0.75);
+ }
+ };
+ AbstractAction actionZoom100 = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setZoom(1.0);
+ }
+ };
+ AbstractAction actionZoom150 = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setZoom(1.5);
+ }
+ };
+ AbstractAction actionZoom200 = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ setZoom(2.0);
+ }
+ };
+ AbstractAction actionSaveImage = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ saveImage();
+ }
+ };
+ AbstractAction actionAbout = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ about();
+ }
+ };
+
+ JComponent targetComponent = getRootPane();
+ InputMap inputMap = targetComponent.getInputMap();
+
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_DOWN_MASK), "Select Device");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK), "Portrait");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK), "Landscape");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_5, InputEvent.CTRL_DOWN_MASK), "50%");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_7, InputEvent.CTRL_DOWN_MASK), "75%");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_1, InputEvent.CTRL_DOWN_MASK), "100%");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, InputEvent.CTRL_DOWN_MASK), "150%");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_2, InputEvent.CTRL_DOWN_MASK), "200%");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK), "Save Image");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), "About MyGlazz");
+
+ targetComponent.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
+
+ targetComponent.getActionMap().put("Select Device", actionSelectDevice);
+ targetComponent.getActionMap().put("Portrait", actionPortrait);
+ targetComponent.getActionMap().put("Landscape", actionLandscape);
+ targetComponent.getActionMap().put("Select Device", actionSelectDevice);
+ targetComponent.getActionMap().put("50%", actionZoom50);
+ targetComponent.getActionMap().put("75%", actionZoom75);
+ targetComponent.getActionMap().put("100%", actionZoom100);
+ targetComponent.getActionMap().put("150%", actionZoom150);
+ targetComponent.getActionMap().put("200%", actionZoom200);
+ targetComponent.getActionMap().put("Save Image", actionSaveImage);
+ targetComponent.getActionMap().put("About MyGlazz", actionAbout);
+ }
+
+ private void initializeAbout() {
+ JMenuItem menuItemAbout = new JMenuItem("About MyGlazz");
+ menuItemAbout.setMnemonic(KeyEvent.VK_A);
+ menuItemAbout.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ about();
+ }
+ });
+ mPopupMenu.add(menuItemAbout);
+ }
+
+ private MouseListener mMouseListener = new MouseListener() {
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ if (SwingUtilities.isRightMouseButton(e)) {
+ mPopupMenu.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+ };
+
+ private WindowListener mWindowListener = new WindowListener() {
+ public void windowOpened(WindowEvent arg0) {
+ }
+
+ public void windowIconified(WindowEvent arg0) {
+ }
+
+ public void windowDeiconified(WindowEvent arg0) {
+ }
+
+ public void windowDeactivated(WindowEvent arg0) {
+ }
+
+ public void windowClosing(WindowEvent arg0) {
+ // TODO
+ // if (mADB != null) {
+ // mADB.terminate();
+ // }
+ }
+
+ public void windowClosed(WindowEvent arg0) {
+ }
+
+ public void windowActivated(WindowEvent arg0) {
+ }
+ };
+
+ public class MainPanel extends JPanel {
+ private BufferedImage mImage;
+
+ public MainPanel() {
+ setBackground(Color.BLACK);
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (mImage != null) {
+ int srcWidth;
+ int srcHeight;
+ int dstWidth;
+ int dstHeight;
+ if (mPortrait) {
+ srcWidth = mRawImageWidth;
+ srcHeight = mRawImageHeight;
+ } else {
+ srcWidth = mRawImageHeight;
+ srcHeight = mRawImageWidth;
+ }
+ dstWidth = (int) (srcWidth * mZoom);
+ dstHeight = (int) (srcHeight * mZoom);
+ if (mZoom == 1.0) {
+ g.drawImage(mImage, 0, 0, dstWidth, dstHeight, 0, 0, srcWidth, srcHeight, null);
+ } else {
+ Image image = mImage.getScaledInstance(dstWidth, dstHeight, Image.SCALE_SMOOTH);
+ if (image != null) {
+ g.drawImage(image, 0, 0, dstWidth, dstHeight, 0, 0, dstWidth, dstHeight, null);
+ }
+ }
+ }
+ }
+
+ public void setFBImage(BufferedImage image) {
+ mImage = image;
+ repaint();
+ }
+
+ public BufferedImage getImage() {
+ return mImage;
+ }
+ }
+
+ @Override
+ public void onReceivedEnvelope(Envelope envelope) {
+ if (envelope.screenshot != null) {
+ if (envelope.screenshot.screenshotBytesG2C != null) {
+ InputStream in = new ByteArrayInputStream(envelope.screenshot.screenshotBytesG2C);
+ try {
+ final BufferedImage image = ImageIO.read(in);
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ setImage(image);
+ }
+ });
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/polysfactory/myglazz/awt/MyGlazz.java b/src/main/java/com/polysfactory/myglazz/awt/MyGlazz.java
new file mode 100644
index 0000000..ca59078
--- /dev/null
+++ b/src/main/java/com/polysfactory/myglazz/awt/MyGlazz.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009-2013 adakoda
+ * Copyright (C) 2009-2013 Poly's Factory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polysfactory.myglazz.awt;
+
+import javax.swing.SwingUtilities;
+
+public class MyGlazz {
+
+ private MainFrame mMainFrame;
+ private static String[] mArgs;
+
+ public MyGlazz() {
+ }
+
+ public void initialize() {
+ mMainFrame = new MainFrame(mArgs);
+ mMainFrame.setLocationRelativeTo(null);
+ mMainFrame.setVisible(true);
+ mMainFrame.setFocusable(true);
+ mMainFrame.selectDevice();
+ }
+
+ public static void main(String[] args) {
+ mArgs = args;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ new MyGlazz().initialize();
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/polysfactory/myglazz/awt/SelectDeviceDialog.java b/src/main/java/com/polysfactory/myglazz/awt/SelectDeviceDialog.java
new file mode 100644
index 0000000..d76f2de
--- /dev/null
+++ b/src/main/java/com/polysfactory/myglazz/awt/SelectDeviceDialog.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009-2013 adakoda
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polysfactory.myglazz.awt;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Frame;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultListModel;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.KeyStroke;
+
+@SuppressWarnings("serial")
+public class SelectDeviceDialog extends JDialog {
+ private JList mList;
+ private JScrollPane mScrollPane;
+ private JButton mOK;
+ private JButton mCancel;
+
+ private DefaultListModel mModel;
+ private boolean mIsOK = false;
+ private int mSelectedIndex = -1;
+
+ public SelectDeviceDialog(Frame owner, boolean modal, ArrayList initialList) {
+ super(owner, modal);
+
+ // Frame
+ {
+ setTitle("Select your Glass");
+ setBounds(0, 0, 240, 164);
+ setResizable(false);
+ }
+
+ // List
+ {
+ // Model
+ {
+ mModel = new DefaultListModel();
+ for (int i = 0; i < initialList.size(); i++) {
+ mModel.addElement(initialList.get(i));
+ }
+ }
+
+ mList = new JList(mModel);
+ if (mModel.getSize() > 0) {
+ mSelectedIndex = 0;
+ mList.setSelectedIndex(mSelectedIndex);
+ }
+ mList.addMouseListener(new MouseListener() {
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() > 1) {
+ onOK();
+ }
+ }
+ });
+
+ // Scroll pane
+ {
+ mScrollPane = new JScrollPane(mList);
+ mScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ }
+ }
+
+ // OK
+ {
+ mOK = new JButton("OK");
+ mOK.setEnabled((mModel.getSize() > 0));
+ mOK.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ onOK();
+ }
+ });
+ }
+
+ // Cancel
+ {
+ mCancel = new JButton("Cancel");
+ mCancel.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ onCancel();
+ }
+ });
+ }
+
+ // Container
+ {
+ Container container1 = new Container();
+ GridLayout gridLayout = new GridLayout(1, 2, 0, 0);
+ container1.setLayout(gridLayout);
+ container1.add(mOK);
+ container1.add(mCancel);
+
+ Container containger = getContentPane();
+ containger.add(mScrollPane, BorderLayout.CENTER);
+ containger.add(container1, BorderLayout.SOUTH);
+ }
+
+ // Key
+ {
+ AbstractAction actionOK = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ onOK();
+ }
+ };
+ AbstractAction actionCancel = new AbstractAction() {
+ public void actionPerformed(ActionEvent e) {
+ onCancel();
+ }
+ };
+
+ JComponent targetComponent = getRootPane();
+ InputMap inputMap = targetComponent.getInputMap();
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "OK");
+ inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
+ targetComponent.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
+ targetComponent.getActionMap().put("OK", actionOK);
+ targetComponent.getActionMap().put("Cancel", actionCancel);
+ }
+ }
+
+ public int getSelectedIndex() {
+ return mSelectedIndex;
+ }
+
+ public boolean isOK() {
+ return mIsOK;
+ }
+
+ private void onOK() {
+ mSelectedIndex = mList.getSelectedIndex();
+ mIsOK = true;
+ dispose();
+ }
+
+ private void onCancel() {
+ dispose();
+ }
+}