/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.codename1.designer;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.impl.javase.JavaSEPortWithSVGSupport;
import com.codename1.ui.plaf.Accessor;
import com.codename1.ui.util.EditableResources;
import com.codename1.ui.util.UIBuilderOverride;
import java.io.IOException;
import java.util.jar.Attributes.Name;
import java.awt.BorderLayout;
import java.awt.Component;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.prefs.Preferences;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* UI and logic allowing us to pick the MIDlet which will appear in the preview
* pane
*
* @author Shai Almog
*/
public class PickMIDlet extends javax.swing.JPanel {
private static CustomComponent[] customComponents;
/** Creates new form PickMIDlet */
public PickMIDlet() {
initComponents();
midletPicker.setRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if(value == null) {
value = "";
} else {
String s = (String)value;
value = s.substring(0, s.indexOf(','));
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
});
Preferences pref = Preferences.userNodeForPackage(ResourceEditorView.class);
jarFile.setText(pref.get("jar", ""));
updateMIDletList();
midletPicker.setSelectedItem(pref.get("midlet", null));
customComponents = null;
}
public static void showPickMIDletDialog(JComponent parent) {
JDialog dlg = new JDialog((JFrame)SwingUtilities.getWindowAncestor(parent));
dlg.setTitle("Pick MIDlet");
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, new PickMIDlet());
dlg.setLocationByPlatform(true);
dlg.pack();
dlg.setVisible(true);
}
public static CustomComponent[] getCustomComponents() {
if(customComponents == null) {
Preferences pref = Preferences.userNodeForPackage(ResourceEditorView.class);
String jar = pref.get("jar", null);
if(jar != null) {
try {
File jarFile = new File(jar);
URLClassLoader cl = URLClassLoader.newInstance(new URL[]{jarFile.toURI().toURL()}, PickMIDlet.class.getClassLoader());
JarFile zip = new JarFile(jarFile);
Enumeration<JarEntry> en = zip.entries();
List<CustomComponent> customList = new ArrayList<CustomComponent>();
while(en.hasMoreElements()) {
JarEntry e = en.nextElement();
String className = e.getName().replace("/", ".");
if(className.endsWith(".class") && !className.startsWith("com.codename1.ui.") && className.indexOf('$') < 0) {
className = className.substring(0, className.length() - 6);
try {
CustomComponent current = new CustomComponent();
current.setCls(cl.loadClass(className));
if(!current.getCls().getName().startsWith("com.codename1.ui")) {
// check that it has a default constructor
com.codename1.ui.Component cmp = (com.codename1.ui.Component)current.getCls().newInstance();
current.setClassName(current.getCls().getName());
current.setCodenameOneBaseClass(current.getCls().getName());
current.setType(cmp.getUIID());
customList.add(current);
UIBuilderOverride.registerCustomComponent(cmp.getUIID(), current.getCls());
}
} catch (Throwable ex) {
}
}
}
customComponents = new CustomComponent[customList.size()];
customList.toArray(customComponents);
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(JFrame.getFrames()[0], "IO Error when accessing MIDlet JAR File: " + ex, "IO Error", JOptionPane.ERROR_MESSAGE);
}
}
}
return customComponents;
}
public static void resetSettings() {
Preferences pref = Preferences.userNodeForPackage(ResourceEditorView.class);
pref.remove("jar");
pref.remove("midlet");
}
public static void startMIDlet(Hashtable themeHash) {
Accessor.setTheme(themeHash);
Preferences pref = Preferences.userNodeForPackage(ResourceEditorView.class);
String jar = pref.get("jar", null);
String midlet = pref.get("midlet", null);
if(jar != null && midlet != null) {
File jarFile = new File(jar);
if(jarFile.exists()) {
try {
URLClassLoader cl = URLClassLoader.newInstance(new URL[]{jarFile.toURI().toURL()}, PickMIDlet.class.getClassLoader());
StringTokenizer tokenizer = new StringTokenizer((String) midlet, " ,");
String s = tokenizer.nextToken();
while(tokenizer.hasMoreTokens()) {
s = tokenizer.nextToken();
}
Class cls = cl.loadClass(s);
JavaSEPortWithSVGSupport.setClassLoader(cls);
EditableResources.setResourcesClassLoader(cls);
Accessor.setTheme(themeHash);
Object app = cls.newInstance();
JarFile zip = new JarFile(jarFile);
Attributes m = zip.getManifest().getMainAttributes();
cls.getDeclaredMethod("init", new Class[] {Object.class}).invoke(app, new Object[] {null});
cls.getDeclaredMethod("start", new Class[0]).invoke(app, new Object[0]);
// there might be an ongoing transition and the form.show() method is serial
Display.getInstance().callSerially(new Runnable() {
public void run() {
if(Display.getInstance().getCurrent() != null) {
Display.getInstance().getCurrent().refreshTheme();
return;
} else {
new Thread() {
public void run() {
try {
sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Display.getInstance().callSerially(this);
}
}.start();
}
}
});
return;
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
JavaSEPortWithSVGSupport.setClassLoader(PickMIDlet.class);
LiveDemo l = new LiveDemo();
l.init(null);
l.start();
Accessor.setTheme(themeHash);
Form current = Display.getInstance().getCurrent();
if(current != null) {
current.refreshTheme();
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jLabel2 = new javax.swing.JLabel();
jarFile = new javax.swing.JTextField();
pickJarFile = new javax.swing.JButton();
jLabel3 = new javax.swing.JLabel();
midletPicker = new javax.swing.JComboBox();
okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
jTextPane1 = new javax.swing.JTextPane();
FormListener formListener = new FormListener();
setName("Form"); // NOI18N
jLabel2.setText("JAR File");
jLabel2.setName("jLabel2"); // NOI18N
jarFile.setEditable(false);
jarFile.setName("jarFile"); // NOI18N
pickJarFile.setText("...");
pickJarFile.setName("pickJarFile"); // NOI18N
pickJarFile.addActionListener(formListener);
jLabel3.setText("MIDlet");
jLabel3.setName("jLabel3"); // NOI18N
midletPicker.setName("midletPicker"); // NOI18N
okButton.setText("OK");
okButton.setEnabled(false);
okButton.setName("okButton"); // NOI18N
okButton.addActionListener(formListener);
cancelButton.setText("Cancel");
cancelButton.setName("cancelButton"); // NOI18N
cancelButton.addActionListener(formListener);
jScrollPane1.setName("jScrollPane1"); // NOI18N
jTextPane1.setContentType("text/html");
jTextPane1.setEditable(false);
jTextPane1.setText("<html>\r\n <head>\r\n\r\n </head>\r\n <body>\r\n <p style=\"margin-top: 0\">\r\n Allows selecting an Application to execute within the preview window of the theme view when\ncreating a theme for a specific Application. The preview might not reflect the full set of changes \nfrom the theme but would provide a good estimate for a designer. This application can also expose\nadditional custom components to the GUI designer view simply by creating valid Codename One component\nsubclasses with default constructors.\r\n </p>\r\n <p>\n The picked Application MUST NOT be obfuscated, this allows the Codename One Designer to replace the \nApplications CodenameOne version with its own. \n </p>\n <p>\n To debug pick Application run the Codename One Designer with a console or from command line and look at the\nerrors printed out when opening a theme.\n </p>\n </body>\r\n</html>\r\n"); // NOI18N
jTextPane1.setName("jTextPane1"); // NOI18N
jScrollPane1.setViewportView(jTextPane1);
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(jLabel2)
.add(jLabel3))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(midletPicker, 0, 274, Short.MAX_VALUE)
.add(jarFile, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE)
.add(layout.createSequentialGroup()
.add(okButton)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cancelButton)))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(pickJarFile)
.addContainerGap())
.add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 452, Short.MAX_VALUE)
);
layout.linkSize(new java.awt.Component[] {cancelButton, okButton}, org.jdesktop.layout.GroupLayout.HORIZONTAL);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(jLabel2)
.add(jarFile, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(pickJarFile))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(jLabel3)
.add(midletPicker, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(okButton)
.add(cancelButton))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 247, Short.MAX_VALUE))
);
}
// Code for dispatching events from components to event handlers.
private class FormListener implements java.awt.event.ActionListener {
FormListener() {}
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (evt.getSource() == pickJarFile) {
PickMIDlet.this.pickJarFileActionPerformed(evt);
}
else if (evt.getSource() == okButton) {
PickMIDlet.this.okButtonActionPerformed(evt);
}
else if (evt.getSource() == cancelButton) {
PickMIDlet.this.cancelButtonActionPerformed(evt);
}
}
}// </editor-fold>//GEN-END:initComponents
private void updateMIDletList() {
String j = jarFile.getText();
if(j != null && j.length() > 0) {
File file = new File(j);
if(file.exists() && file.isFile()) {
try {
JarFile zip = new JarFile(file);
Attributes m = zip.getManifest().getMainAttributes();
Vector v = new Vector();
for (int i = 1 ; m.getValue("MIDlet-" + i) != null ; i++) {
v.addElement(m.getValue("MIDlet-" + i));
}
midletPicker.setModel(new DefaultComboBoxModel(v));
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "An IO Error occured while accessing\n" +
"the MIDlet JAR:\n" + ex.getMessage(), "IO Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}
private void selectFile(JTextField field, String type) {
JFileChooser c = ResourceEditorView.createFileChooser("*" + type, type);
if(JFileChooser.APPROVE_OPTION == c.showDialog(this, "Select")) {
File f = c.getSelectedFile();
field.setText(f.getAbsolutePath());
Preferences.userNodeForPackage(ResourceEditorView.class).put("lastDir", f.getParentFile().getAbsolutePath());
}
updateMIDletList();
if(midletPicker.getModel().getSize() > 0) {
midletPicker.setSelectedIndex(0);
}
updateOKEnabled();
}
private void updateOKEnabled() {
if(new File(jarFile.getText()).exists()) {
okButton.setEnabled(true);
}
}
private void pickJarFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pickJarFileActionPerformed
selectFile(jarFile, ".jar");
}//GEN-LAST:event_pickJarFileActionPerformed
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
SwingUtilities.windowForComponent(this).dispose();
Preferences pref = Preferences.userNodeForPackage(ResourceEditorView.class);
pref.put("jar", jarFile.getText());
pref.put("midlet", (String)midletPicker.getSelectedItem());
}//GEN-LAST:event_okButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelButton;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextPane jTextPane1;
private javax.swing.JTextField jarFile;
private javax.swing.JComboBox midletPicker;
private javax.swing.JButton okButton;
private javax.swing.JButton pickJarFile;
// End of variables declaration//GEN-END:variables
}