/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community 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.osedu.org/licenses/ECL-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 tufts.vue;
import java.lang.reflect.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import javax.swing.border.*;
class LWCInspector extends javax.swing.JPanel
implements VueConstants,
LWSelection.Listener,
LWComponent.Listener,
ActionListener
{
private JLabel idField = new JLabel();
private JLabel typeField = new JLabel();
private JLabel parentField = new JLabel();
private JLabel locationField = new JLabel();
private JLabel sizeField = new JLabel();
private JLabel labelHex = new JLabel();
private JLabel styleField = new JLabel();
private JLabel syncField = new JLabel();
private JTextField labelField = new JTextField(15);
private JTextField widthField = new JTextField();
private JTextField heightField = new JTextField();
private JTextField xField = new JTextField();
private JTextField yField = new JTextField();
private JTextField transLocField = new JTextField();
private JTextField bitsField = new JTextField();
private JTextField zoomField = new JTextField();
private JTextField categoryField = new JTextField();
private JTextField resourceTypeField = new JTextField();
private JTextField resourceField = new JTextField();
private JTextField notesField = new JTextField();
//private JPanel extraPanel = new JPanel();
private boolean LOG_ENABLED = false;
private JTextArea log = new JTextArea() {
int rows = 0;
public void append(String s) {
if (LOG_ENABLED)
super.append(rows++ + " " + s + "\n");
}
};
//private JTextArea notesField = new JTextArea(1, 20);
private JPanel fieldPane = new JPanel();
private JPanel resourceMetadataPanel = new JPanel();
private JPanel metadataPane = new JPanel();
private LinkedList<LWCEvent> events = new LinkedList();
//String[] labels = { "ID", "<html><font color=red>Label</font></html>", "Category", "Resource", "Notes" };
private Object[] labelTextPairs = {
"-ID", idField,
"-Type", typeField,
"-Parent", parentField,
"-Location",locationField,
"-Size", sizeField,
"-ZeroTX", transLocField,
"-Bits", bitsField,
"-Style", styleField,
"-Sync", syncField,
"Label", labelField,
"Width", widthField,
"Height", heightField,
"X", xField,
"Y", yField,
"Scale", zoomField,
"-ResourceType", resourceTypeField,
"Resource", resourceField,
//"Category", categoryField,
//"-Notes", notesField,
};
private java.util.Map<JTextField,LWComponent.Key> fieldKeys = new java.util.HashMap();
private JCheckBox lockBtn = new JCheckBox("Lock");
public LWCInspector()
{
//extraPanel.setLayout(new BorderLayout());
//extraPanel.setSize(200,100);
//extraPanel.add(new JLabel("foo"));
GridBagLayout gridBag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
fieldPane.setLayout(gridBag);
/*
c.gridwidth = GridBagConstraints.REMAINDER;
c.anchor = GridBagConstraints.WEST;
c.weightx = 1.0;
//gridbag.setConstraints(actionLabel, c);
//textControlsPane.add(actionLabel);
*/
if (!(notesField instanceof JTextField))
notesField.setBorder(LineBorder.createGrayLineBorder());
java.util.List pairsAndKeys = new java.util.ArrayList();
pairsAndKeys.addAll(Arrays.asList(labelTextPairs));
pairsAndKeys.addAll(LWComponent.Key.AllKeys);
addLabelTextRows(pairsAndKeys, gridBag, fieldPane);
//addLabelTextRows(labelTextPairs, gridBag, fieldPane);
// settting metadata
//setUpMetadataPane();
setLayout(new BorderLayout());
if (DEBUG.META) {
add(new JScrollPane(log), BorderLayout.NORTH);
log.setFont(new Font("Lucida Sans Typewriter", Font.BOLD, 9));
log.setRows(15);
log.setEditable(false);
LOG_ENABLED = true;
}
add(fieldPane, BorderLayout.CENTER);
add(lockBtn, BorderLayout.SOUTH);
//add(metadataPane,BorderLayout.SOUTH);
//VUE.ModelSelection.addListener(this);
VUE.addActiveListener(LWPathway.Entry.class, this);
VUE.addActiveListener(LWComponent.class, this);
}
// public void activeChanged(ActiveEvent e, LWPathway path) {
// if (lockBtn.isSelected())
// return;
// if (path != null)
// loadItem(path);
// }
public void activeChanged(ActiveEvent e, LWPathway.Entry entry) {
if (entry != null && entry.isPathway() && VUE.getSelection().isEmpty())
loadItem(entry.pathway.getMasterSlide());
}
public void activeChanged(ActiveEvent e, LWComponent c) {
if (lockBtn.isSelected())
return;
if (c == null)
loadItem(VUE.getActiveMap());
else if (c instanceof LWPathway)
loadItem(((LWPathway)c).getMasterSlide());
else
loadItem(c);
}
private void setUpMetadataPane() {
BoxLayout layout = new BoxLayout(metadataPane,BoxLayout.Y_AXIS);
metadataPane.setLayout(layout);
metadataPane.add(resourceMetadataPanel);
}
private static final String KEY_KEY = "LWKEY";
//private void addLabelTextRows(Object[] labelTextPairs,
private void addLabelTextRows(java.util.List pairsAndKeys,
GridBagLayout gridbag,
Container container)
{
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.EAST;
int num = labelTextPairs.length;
//for (int i = 0; i < num; i += 2) {
Iterator i = pairsAndKeys.iterator();
while (i.hasNext()) {
Object item = i.next();
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
c.anchor = GridBagConstraints.EAST;
String txt;
final JComponent field;
if (item instanceof String) {
txt = (String) item;
field = (JComponent) i.next();
} else {
LWComponent.Key key = (LWComponent.Key) item;
txt = key.name;
final JTextField textField = new JTextField();
fieldKeys.put(textField, key);
field = textField;
}
//String txt = (String) labelTextPairs[i];
//JComponent field = (JComponent) labelTextPairs[i+1];
boolean readOnly = false;
if (txt.startsWith("-")) {
txt = txt.substring(1);
readOnly = true;
}
txt += ": ";
JLabel label = new JLabel(txt);
//label.setFont(VueConstants.SmallFont);
label.setFont(VueConstants.FONT_NARROW);
gridbag.setConstraints(label, c);
container.add(label);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
field.setFont(VueConstants.FixedSmallFont);
if (field instanceof JTextField)
((JTextField)field).addActionListener(this);
gridbag.setConstraints(field, c);
container.add(field);
if (readOnly) {
field.setBorder(new EmptyBorder(1,1,1,1));
if (field instanceof JTextField) {
JTextField tf = (JTextField) field;
tf.setEditable(false);
tf.setFocusable(false);
}
if (VueUtil.isMacPlatform())
field.setBackground(SystemColor.control);
}
}
/*
JComponent field = new JLabel("Metadata");
c.gridwidth = GridBagConstraints.RELATIVE; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.WEST;
gridbag.setConstraints(field, c);
container.add(field);
*/
}
/*
String s = key + " " + source;
if (component != null && component != source)
s += " c:" + component;
//basic information.. if more information wants to be stringfied, need to code this part
else if (components != null)
s += " l:" + components;
//s += " ArrayList";
return s;
*/
public void LWCChanged(LWCEvent e)
{
if (!isShowing())
return;
String extra = "";
if (e.component != null && e.component != e.source)
extra += " from:" + e.component;
if (e.getComponents() != null)
extra += " list:" + e.getComponents();
log.append(String.format("%-21s %s" + extra, e.key, e.source));
//System.out.println(this + " " + e);
if (this.lwc != e.getSource())
return;
if (e.key == LWKey.Deleting) {
this.lwc = null;
//loadItem(null);
setAllEnabled(false);
} else if (e.getSource() != this) {
loadItem(this.lwc);
//log.setText(null);
} //else
}
public void selectionChanged(LWSelection selection)
{
if (!lockBtn.isSelected())
setSelection(selection);
}
private void loadText(JTextComponent c, Object o)
{
String text = (o == null ? "null" : o.toString());
String hasText = c.getText();
// This prevents flashing where fields of
// length greater the the visible area do
// a flash-scroll when setting the text, even
// if it's the same as what's there.
if (hasText != text && !hasText.equals(text))
c.setText(text);
}
private LWComponent lwc; // temporary
public void setSelection(LWSelection selection)
{
//System.err.println("Inspector setSelection: " + sl);
if (selection.size() == 1) {
log.append("LOADED: " + selection.first() + "\n");
loadItem(selection.first());
} else {// (!selection.isEmpty())
log.append("LOADED: " + selection + "\n");
loadSelection(selection);
}
}
private void setAllEnabled(boolean tv)
{
int pairs = labelTextPairs.length;
for (int i = 0; i < pairs; i += 2) {
JComponent field = (JComponent) labelTextPairs[i+1];
field.setEnabled(tv);
}
}
private void disable(JTextComponent tc)
{
tc.setText("");
tc.setEnabled(false);
}
private void disable(JLabel l)
{
l.setText("");
l.setEnabled(false);
}
private void loadSelection(LWSelection selection)
{
if (selection.isEmpty()) {
loadItem(VUE.getActiveMap());
return;
}
setAllEnabled(true);
String id = "";
if (selection.allOfSameType())
id += selection.first().getClass().getName();
else
id += "LWComponent";
id += "[" + selection.size() + "]";
LWComponent c = selection.first();
// grab first in selection for moment
idField.setText(id);
labelField.setBackground(Color.white);
loadText(labelField, "");
loadText(categoryField, "");
disable(notesField);
disable(resourceField);
disable(locationField);
disable(sizeField);
}
private void loadLabel(LWComponent lwc) {
Color c = lwc.getRenderFillColor(null);
if (c == null || c.getAlpha() != 255)
c = Color.gray;
labelField.setBackground(c);
if (Color.black.equals(c))
labelField.setForeground(Color.white);
else
labelField.setForeground(Color.darkGray);
// if (c.isTranslucent() || c.getRenderFillColor() == null)
// labelField.setBackground(Color.gray);
// else
// labelField.setBackground(c.getRenderFillColor());
loadText(labelField, lwc.getLabel());
}
private void loadItem(LWComponent lwc)
{
if (this.lwc != lwc) {
if (this.lwc != null)
this.lwc.removeLWCListener(this);
this.lwc = lwc;
if (this.lwc != null) {
this.lwc.addLWCListener(this);
setAllEnabled(true);
} else
setAllEnabled(false);
}
//System.out.println(this + " loadItem " + lwc);
LWComponent c = this.lwc;
if (c == null)
return;
setAllEnabled(true);
//System.out.println(this + " loading " + c);
if (c.getResource() != null) {
loadText(resourceField, c.getResource().toString());
loadText(resourceTypeField, c.getResource().getClass().getName());
} else {
loadText(resourceField, "");
loadText(resourceTypeField, "");
}
String id = c.getID();
if (c.getParent() == null)
id += " [PARENT IS NULL]";
else {
id += ", #" + c.getParent().indexOf(c);
id += " in <" + c.getParent().toName() + ">";
id += " links:" + c.getLinks().size();
if (c instanceof LWContainer && c.hasChildren())
id += " children:" + ((LWContainer)c).getChildList().size();
}
idField.setText(id);
if (lwc.getTypeToken() != lwc.getClass())
typeField.setText(lwc.getClass().getName() + " [" + lwc.getTypeToken() + "]");
else
typeField.setText(lwc.getClass().getName());
parentField.setText(c.getParent() == null ? "null" : c.getParent().toString());
loadLabel(c);
loadText(notesField, c.getNotes());
locationField.setText("x: " + c.getX() + " y: " + c.getY());
String sizeText = String.format("%.0fx%.0f", c.getWidth(), c.getHeight());
if (true||c.getScale() != 1f) {
sizeText += " z=" + c.getScale();
sizeText += " mapZ=" + c.getMapScale();
}
if (!c.isAutoSized())
sizeText += " userSize";
sizeField.setText(sizeText);
transLocField.setText(c.getZeroTransform().toString());
bitsField.setText(c.getDescriptionOfSetBits() + (c.isFiltered() ? " +FILTERED" : ""));
styleField.setText(""+c.getStyle());
syncField.setText(""+c.getSyncSource());
widthField.setText(String.format("%5.1f map(%5.1f)", c.getWidth(), c.getMapWidth()));
heightField.setText(String.format("%5.1f map(%5.1f)", c.getHeight(), c.getMapHeight()));
xField.setText(String.format("%5.1f map(%5.1f)", c.getX(), c.getMapX()));
yField.setText(String.format("%5.1f map(%5.1f)", c.getY(), c.getMapY()));
zoomField.setText(""+c.getScale());
for (Map.Entry<JTextField,LWComponent.Key> e : fieldKeys.entrySet()) {
final JTextField field = e.getKey();
final LWComponent.Key key = e.getValue();
try {
if (DEBUG.PROPERTY || c.supportsProperty(key))
field.setText(key.getStringValue(c));
else
field.setText("<unsupported for " + c.getClass().getName() + ">");
} catch (Throwable t) {
if (field != null)
field.setText(t.toString());
t.printStackTrace();
}
}
//loading the metadata if it exists
/*
if(c.getResource() != null && c.getResource().getProperties() != null) {
metadataPane.remove(resourceMetadataPanel);
if(c.getResource().getType() == Resource.ASSET_FEDORA)
resourceMetadataPanel = new PropertiesEditor(c.getResource().getProperties(), false);
else
resourceMetadataPanel = new PropertiesEditor(c.getResource().getProperties(), true);
metadataPane.add(resourceMetadataPanel);
}
*/
/*
if (false&&c.labelBox != null) {
//extraPanel.add(p);
if (lastp != null) {
//System.out.println("REMOVING " + lastp);
VUE.toolPanel.remove(lastp);
}
JPanel np = new JPanel();
//System.out.println("ADDING " + p);
VUE.toolPanel.add(np, BorderLayout.CENTER);
System.out.println("ADDING MTP TO VUE for " + c);
np.add(c.labelBox);
lastp = np;
}
*/
}
LWSelection dummySelection = (LWSelection) new LWSelection().clone(); // clone so won't set selection bits
private LWSelection getSelection() {
if (VUE.getSelection().isEmpty()) {
dummySelection.setTo(this.lwc);
return dummySelection;
} else
return VUE.getSelection();
}
private void setWidths(String text)
throws NumberFormatException
{
float w = Float.parseFloat(text);
for (LWComponent c : getSelection())
c.setSize(w, c.getHeight());
//c.setAbsoluteSize(w, c.getAbsoluteHeight());
}
private void setHeights(String text)
throws NumberFormatException
{
float h = Float.parseFloat(text);
Iterator i = getSelection().iterator();
while (i.hasNext()) {
LWComponent c = (LWComponent) i.next();
c.setSize(c.getWidth(), h);
//c.setAbsoluteSize(c.getAbsoluteWidth(), h);
}
}
private void setXs(String text)
throws NumberFormatException
{
float v = Float.parseFloat(text);
Iterator i = getSelection().iterator();
while (i.hasNext()) {
LWComponent c = (LWComponent) i.next();
c.setLocation(v, c.getY());
}
}
private void setYs(String text)
throws NumberFormatException
{
float v = Float.parseFloat(text);
Iterator i = getSelection().iterator();
while (i.hasNext()) {
LWComponent c = (LWComponent) i.next();
c.setLocation(c.getX(), v);
}
}
private void setScales(String text)
throws NumberFormatException
{
float s = Float.parseFloat(text);
Iterator i = getSelection().iterator();
while (i.hasNext()) {
LWComponent c = (LWComponent) i.next();
c.setScale(s);
}
}
public void actionPerformed(ActionEvent e)
{
//tufts.macosx.Screen.dumpMainMenu();
if (this.lwc == null)
return;
final String text = e.getActionCommand(); // text is in the action command??
final JComponent src = (JTextComponent) e.getSource();
final LWComponent c = this.lwc;
//System.out.println("Inspector " + e);
LWComponent.Key key;
try {
boolean set = true;
if (src == labelField) c.setLabel(text);
//else if (src == categoryField) c.setCategory(text);
//else if (src == notesField) c.setNotes(text);
else if (src == widthField) setWidths(text);
else if (src == heightField) setHeights(text);
else if (src == zoomField) setScales(text);
else if (src == resourceField) c.setResource(text);
else if (src == xField) setXs(text);
else if (src == yField) setYs(text);
// else if (src == strokeField) {
// float w = Float.parseFloat(text);
// c.setStrokeWidth(w);
// }
//else if ((key = (LWComponent.Key) src.getClientProperty(KEY_KEY)) != null) {
else if ((key = fieldKeys.get(src)) != null) {
key.setStringValue(c, text);
} else
set = false;
if (set)
VUE.getUndoManager().mark();
else
return;
} catch (Exception ex) {
System.err.println(ex);
System.err.println("LWCInspector: error setting property value ["+text+"] on " + src);
}
transferFocus(); // this isn't going to next field
//todo: getNextFocusableComponent().requestFocus();
// Try below with next-focus action for enter?
// component.getInputMap().put(aKeyStroke, aCommand);
// component.getActionMap().put(aCommmand, anAction);
// Or: just have something else request focus.
// Could even track to the next component ourself,
// tho it seems rediculous to manage ourselves given
// there's a BOODLE of awt code for this -- so
// why isn't this dead easy???
}
public String toString()
{
return "LWCInspector@" + Integer.toHexString(hashCode());
}
static class Introspector extends javax.swing.JPanel
implements ActiveListener
{
final StringBuffer text = new StringBuffer();
final JTextArea textArea = new JTextArea();
final Map<Class,Object> allActive = new HashMap();
// todo: keep sorted so we can see order of active items
public Introspector() {
setLayout(new BorderLayout());
setBorder(new EmptyBorder(2,2,2,2));
ActiveInstance.addAllActiveListener(this);
textArea.setFont(VueConstants.LargeFixedFont);
add(new JScrollPane(textArea,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
));
}
public synchronized void activeChanged(ActiveEvent e) {
allActive.put(e.type, e.active);
updateText();
}
private void updateText() {
text.setLength(0);
for (Map.Entry<Class,Object> e : allActive.entrySet()) {
final Class implClass = e.getValue() == null ? null : e.getValue().getClass();
//final String className = (e.getValue() == null ? "" : e.getValue().getClass.getName());
text.append(String.format("\n%25s: %s%s\n",
e.getKey().getName(),
(implClass == e.getKey() || implClass == null) ? "" : ("(" + implClass.getName() + ") "),
e.getValue()));
if (DEBUG.META)
addMethodData(e.getValue());
//text.append(e.getKey().getName() + ": " + e.getValue() + "\n");
}
textArea.setText(text.toString());
}
private void addMethodData(Object instance) {
if (instance == null)
return;
final Class clazz = instance.getClass();
final Method[] methods;
if (LWComponent.class.isInstance(instance) ||
VueTool.class.isInstance(instance)
)
methods = clazz.getMethods();
else
methods = clazz.getDeclaredMethods(); // includes private's, excludes super-class methods
for (Method m : methods) {
if (m.getReturnType() == void.class || m.getParameterTypes().length > 0)
continue;
//text.append("\t" + m.getName() + "\n");
//text.append(String.format("\t%32-s %-30s %s\n",
Object value = null;
if ("duplicate".equals(m.getName()) || "clone".equals(m.getName()))
value = "not invoked:" + m.getName();
try {
if (value == null)
value = m.invoke(instance);
} catch (java.lang.reflect.InvocationTargetException t) {
value = t.getCause();
} catch (Throwable t) {
//tufts.Util.printStackTrace(t, m + ": " + instance);
value = t;
}
if (false)
text.append("\t" + m + "\n");
else
text.append(String.format("%-23s %-30s %s\n",
shortTypeName(m.getReturnType()),
m.getName(),
value));
}
}
private static String shortTypeName(Class type) {
String name = type.getName();
if (name.indexOf('.') > 0)
return name.substring(name.lastIndexOf('.')+1, name.length());
else
return name;
}
}
}