/*
* Copyright (c) 2012, Codename One 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. Codename One 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 Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.impl.javase;
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.list.ContainerList;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JTree;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
/**
* A visual view of the component hierarchy within the current Codename One form
* this UI monitors the tree of components and gives us some information of its
* structure which can be useful for debugging applications.
*
* @author Shai Almog
*/
public class ComponentTreeInspector extends javax.swing.JFrame {
private List<String> themePaths = new ArrayList<String>();
private List<String> themeNames = new ArrayList<String>();
private Component currentComponent;
/** Creates new form ComponentTreeInspector */
public ComponentTreeInspector() {
initComponents();
File[] resFiles = new File("src").listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".res");
}
});
themes.removeAllItems();
for(File r : resFiles) {
try {
Resources rr = Resources.open("/" + r.getName());
for(String themeName : rr.getThemeResourceNames()) {
themes.addItem(r.getName() + " - " + themeName);
themePaths.add(r.getAbsolutePath());
themeNames.add(themeName);
}
} catch(IOException err) {
err.printStackTrace();
}
}
refreshComponentTree();
componentUIID.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
updateUiid();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateUiid();
}
@Override
public void changedUpdate(DocumentEvent e) {
updateUiid();
}
private void updateUiid() {
final String uiidText = componentUIID.getText();
if(currentComponent != null && uiidText.length() > 0) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
if(currentComponent != null && !currentComponent.getUIID().equals(uiidText)) {
currentComponent.setUIID(uiidText);
Display.getInstance().getCurrent().revalidate();
}
}
});
}
}
});
componentTree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component t = (Component)value;
String newVal = t.getClass().getSimpleName();
if(t.getName() != null) {
newVal += "[" + t.getName() + "]";
} else {
newVal += "[Unnamed]";
}
newVal += ", " + t.getUIID();
return super.getTreeCellRendererComponent(tree, newVal, sel, expanded, leaf, row, hasFocus);
}
});
componentTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
if(e.getPath() != null) {
Component c = (Component)e.getPath().getLastPathComponent();
if(c != null) {
currentComponent = c;
componentClass.setText(c.getClass().getName());
componentName.setText("" + c.getName());
componentUIID.setText("" + c.getUIID());
componentSelected.setSelected(c.hasFocus());
coordinates.setText("x: " + c.getX() + " y: " + c.getY() + " absX: " + c.getAbsoluteX()+ " absY: " + c.getAbsoluteY() +
" Width: " + c.getWidth() + " Height: " + c.getHeight());
preferredSize.setText(c.getPreferredW() + ", " + c.getPreferredH());
padding.setText("Top: " + c.getStyle().getPadding(Component.TOP) + " Bottom: " + c.getStyle().getPadding(Component.BOTTOM)
+ " Left: " + c.getStyle().getPadding(Component.LEFT) + " Right: " + c.getStyle().getPadding(Component.RIGHT));
margin.setText("Top: " + c.getStyle().getMargin(Component.TOP) + " Bottom: " + c.getStyle().getMargin(Component.BOTTOM)
+ " Left: " + c.getStyle().getMargin(Component.LEFT) + " Right: " + c.getStyle().getMargin(Component.RIGHT));
if(c instanceof com.codename1.ui.Container) {
layout.setText(((com.codename1.ui.Container)c).getLayout().getClass().getSimpleName());
} else {
layout.setText("");
}
com.codename1.ui.Container parent = c.getParent();
constraint.setText("");
if(parent != null) {
Object o = parent.getLayout().getComponentConstraint(c);
if(o != null) {
constraint.setText(o.toString());
}
}
}
}
}
});
pack();
setLocationByPlatform(true);
setVisible(true);
}
private void refreshComponentTree() {
componentTree.setModel(new ComponentTreeModel(Display.getInstance().getCurrent()));
}
/** 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() {
jSplitPane1 = new javax.swing.JSplitPane();
jScrollPane1 = new javax.swing.JScrollPane();
componentTree = new javax.swing.JTree();
jPanel1 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
jLabel3 = new javax.swing.JLabel();
jLabel4 = new javax.swing.JLabel();
componentClass = new javax.swing.JTextField();
componentName = new javax.swing.JTextField();
componentUIID = new javax.swing.JTextField();
componentSelected = new javax.swing.JCheckBox();
jLabel5 = new javax.swing.JLabel();
layout = new javax.swing.JTextField();
jLabel6 = new javax.swing.JLabel();
constraint = new javax.swing.JTextField();
jLabel7 = new javax.swing.JLabel();
coordinates = new javax.swing.JTextField();
jLabel8 = new javax.swing.JLabel();
preferredSize = new javax.swing.JTextField();
jLabel9 = new javax.swing.JLabel();
jLabel10 = new javax.swing.JLabel();
padding = new javax.swing.JTextField();
margin = new javax.swing.JTextField();
unselected = new javax.swing.JButton();
themes = new javax.swing.JComboBox();
jToolBar1 = new javax.swing.JToolBar();
refreshTree = new javax.swing.JButton();
FormListener formListener = new FormListener();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle("Component Tree Inspector");
jScrollPane1.setViewportView(componentTree);
jSplitPane1.setLeftComponent(jScrollPane1);
jLabel1.setText("Class");
jLabel2.setText("Name");
jLabel3.setText("UIID");
jLabel4.setText("Selected");
componentClass.setEditable(false);
componentName.setEditable(false);
componentSelected.setEnabled(false);
jLabel5.setText("Layout");
layout.setEditable(false);
jLabel6.setText("Constraint");
jLabel6.setToolTipText("Layout Constraint (for border layout)");
constraint.setEditable(false);
constraint.setToolTipText("Layout Constraint (for border layout)");
jLabel7.setText("Coordinates");
coordinates.setEditable(false);
jLabel8.setText("Preferred Size");
preferredSize.setEditable(false);
jLabel9.setText("Padding");
jLabel10.setText("Margin");
padding.setEditable(false);
margin.setEditable(false);
unselected.setText("Edit");
unselected.addActionListener(formListener);
themes.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel5)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel4)
.addComponent(jLabel3)
.addComponent(jLabel2)
.addComponent(jLabel1)
.addComponent(jLabel6)
.addComponent(jLabel7)
.addComponent(jLabel8)
.addComponent(jLabel9)
.addComponent(jLabel10))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(componentClass)
.addComponent(componentName)
.addComponent(layout)
.addComponent(constraint)
.addComponent(coordinates)
.addComponent(preferredSize)
.addComponent(padding)
.addComponent(margin)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(componentSelected)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(componentUIID, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(themes, 0, 162, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(unselected, javax.swing.GroupLayout.PREFERRED_SIZE, 94, javax.swing.GroupLayout.PREFERRED_SIZE)))))
.addContainerGap())
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(componentClass, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(componentName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel3)
.addComponent(componentUIID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(unselected)
.addComponent(themes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel4)
.addComponent(componentSelected))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel5)
.addComponent(layout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel6)
.addComponent(constraint, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel7)
.addComponent(coordinates, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel8)
.addComponent(preferredSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel9)
.addComponent(padding, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel10)
.addComponent(margin, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(33, Short.MAX_VALUE))
);
jSplitPane1.setRightComponent(jPanel1);
getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);
jToolBar1.setRollover(true);
refreshTree.setText("Refresh");
refreshTree.setFocusable(false);
refreshTree.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
refreshTree.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
refreshTree.addActionListener(formListener);
jToolBar1.add(refreshTree);
getContentPane().add(jToolBar1, java.awt.BorderLayout.PAGE_START);
pack();
}
// 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() == refreshTree) {
ComponentTreeInspector.this.refreshTreeActionPerformed(evt);
}
else if (evt.getSource() == unselected) {
ComponentTreeInspector.this.unselectedActionPerformed(evt);
}
}
}// </editor-fold>//GEN-END:initComponents
private void refreshTreeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshTreeActionPerformed
refreshComponentTree();
}//GEN-LAST:event_refreshTreeActionPerformed
private void unselectedActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unselectedActionPerformed
editStyle();
}//GEN-LAST:event_unselectedActionPerformed
private void editStyle() {
File cn1dir = new File(System.getProperty("user.home"), ".codenameone");
if(!cn1dir.exists()) {
JOptionPane.showMessageDialog(this, "Please open the designer once by opening the theme.res file", "Error Opening Designer", JOptionPane.ERROR_MESSAGE);
return;
}
File resourceEditor = new File(cn1dir, "designer_1.jar");
if(!resourceEditor.exists()) {
resourceEditor = new File(cn1dir, "designer.jar");
}
if(!resourceEditor.exists()) {
JOptionPane.showMessageDialog(this, "Please open the designer once by opening the theme.res file", "Error Opening Designer", JOptionPane.ERROR_MESSAGE);
return;
}
File javaBin = new File(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java.exe");
if(!javaBin.exists()) {
javaBin = new File(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}
final File javaExe = javaBin;
final File resourceEditorFinal = resourceEditor;
final String themeFile = themePaths.get(themes.getSelectedIndex());
final String themeName = themeNames.get(themes.getSelectedIndex());
final String uiid = componentUIID.getText();
new Thread() {
@Override
public void run() {
try {
ProcessBuilder pb = new ProcessBuilder(javaExe.getAbsolutePath(), "-jar",
resourceEditorFinal.getAbsolutePath(), "-style", themeFile, uiid, themeName).inheritIO();
Process proc = pb.start();
proc.waitFor();
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
try {
Resources res = Resources.open(new FileInputStream(themeFile));
UIManager.getInstance().addThemeProps(res.getTheme(themeName));
Display.getInstance().getCurrent().refreshTheme();
Display.getInstance().getCurrent().revalidate();
} catch(Exception err) {
err.printStackTrace();
}
}
});
} catch(Exception err) {
err.printStackTrace();
}
}
}.start();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField componentClass;
private javax.swing.JTextField componentName;
private javax.swing.JCheckBox componentSelected;
private javax.swing.JTree componentTree;
private javax.swing.JTextField componentUIID;
private javax.swing.JTextField constraint;
private javax.swing.JTextField coordinates;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel10;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel jLabel8;
private javax.swing.JLabel jLabel9;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JToolBar jToolBar1;
private javax.swing.JTextField layout;
private javax.swing.JTextField margin;
private javax.swing.JTextField padding;
private javax.swing.JTextField preferredSize;
private javax.swing.JButton refreshTree;
private javax.swing.JComboBox themes;
private javax.swing.JButton unselected;
// End of variables declaration//GEN-END:variables
}