blob: e4cc44f952a64171d321b87afd696525251e7d3b [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.intellij.internal.inspector;
import com.intellij.icons.AllIcons;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.notification.NotificationsManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.StripeTable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.ColorIcon;
import com.intellij.util.ui.PlatformColors;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
/**
* User: spLeaner
*/
public class UiInspectorAction extends ToggleAction implements DumbAware {
private UiInspector myInspector = null;
@Override
public boolean isSelected(AnActionEvent e) {
return myInspector != null;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
if (state) {
if (myInspector == null) {
myInspector = new UiInspector();
Toolkit.getDefaultToolkit().addAWTEventListener(myInspector, AWTEvent.MOUSE_EVENT_MASK);
}
UiInspectorNotification[] existing =
NotificationsManager.getNotificationsManager().getNotificationsOfType(UiInspectorNotification.class, null);
if (existing.length == 0) {
Notifications.Bus.notify(new UiInspectorNotification(), null);
}
}
else {
if (myInspector != null) {
Toolkit.getDefaultToolkit().removeAWTEventListener(myInspector);
Disposer.dispose(myInspector);
myInspector = null;
}
}
}
private static class UiInspectorNotification extends Notification {
private UiInspectorNotification() {
super(Notifications.SYSTEM_MESSAGES_GROUP_ID, "UI Inspector", "Control-Alt-Click to view component info!",
NotificationType.INFORMATION);
}
}
private static class InspectorWindow extends JDialog {
private InspectorTable myInspectorTable;
private UiInspector myUiInspector;
private JComponent myComponent;
private boolean myHighlighted = true;
private HighlightComponent myHighlightComponent;
private HierarchyTree myHierarchyTree;
private final JPanel myWrapperPanel;
private InspectorWindow(@NotNull final JComponent component, UiInspector uiInspector) throws HeadlessException {
super(findWindow(component));
final Window window = findWindow(component);
setModal(window instanceof JDialog && ((JDialog)window).isModal());
myComponent = component;
myUiInspector = uiInspector;
getRootPane().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
final String simpleName = component.getClass().getSimpleName();
setTitle(simpleName.length() == 0 ? component.getClass().getName() : simpleName);
JToolBar bar = new JToolBar(SwingConstants.HORIZONTAL);
bar.setFloatable(false);
bar.add(new AbstractAction("Highlight", AllIcons.Toolbar.Unknown) {
public void actionPerformed(ActionEvent e) {
myHighlighted = !myHighlighted;
highlight(myComponent, !myHighlighted);
}
@Override
public boolean isEnabled() {
return myComponent.isVisible();
}
});
bar.addSeparator();
bar.add(new AbstractAction("Refresh", AllIcons.Actions.Refresh) {
public void actionPerformed(ActionEvent e) {
getCurrentTable().refresh();
}
@Override
public boolean isEnabled() {
return myComponent.isVisible();
}
});
add(bar, BorderLayout.NORTH);
myWrapperPanel = new JPanel(new BorderLayout());
add(new JLabel(component.getClass().getName()), BorderLayout.SOUTH);
myInspectorTable = new InspectorTable(component);
myHierarchyTree = new HierarchyTree(component) {
@Override
public void onComponentChanged(Component c) {
highlight(myComponent, true);
switchInfo(c);
if (myHighlighted) highlight(c, false);
}
};
myWrapperPanel.add(myInspectorTable, BorderLayout.CENTER);
JSplitPane splitPane = new JSplitPane();
splitPane.setDividerLocation(0.5);
splitPane.setRightComponent(myWrapperPanel);
JScrollPane pane = new JBScrollPane(myHierarchyTree);
splitPane.setLeftComponent(pane);
add(splitPane, BorderLayout.CENTER);
myHierarchyTree.expandPath();
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
myUiInspector.closed(InspectorWindow.this);
close();
}
});
getRootPane().getActionMap().put("CLOSE", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
myUiInspector.closed(InspectorWindow.this);
close();
InspectorWindow.this.setVisible(false);
InspectorWindow.this.dispose();
}
});
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "CLOSE");
}
private static Window findWindow(JComponent component) {
final DialogWrapper dialogWrapper = DialogWrapper.findInstance(component);
if (dialogWrapper != null) {
return dialogWrapper.getPeer().getWindow();
}
return null;
}
private InspectorTable getCurrentTable() {
return myInspectorTable;
}
private void switchInfo(Component c) {
myWrapperPanel.removeAll();
if (c != null) {
myInspectorTable = new InspectorTable(c);
myWrapperPanel.add(myInspectorTable, BorderLayout.CENTER);
}
myWrapperPanel.revalidate();
myWrapperPanel.repaint();
}
public void close() {
highlight(myComponent, true);
myComponent = null;
}
private void highlight(final Component c, final boolean clear) {
if (c != null) {
final JRootPane rootPane = SwingUtilities.getRootPane(c);
if (rootPane != null) {
final JComponent glassPane = (JComponent)rootPane.getGlassPane();
if (clear) {
if (myHighlightComponent != null) {
glassPane.remove(myHighlightComponent);
myHighlightComponent = null;
glassPane.revalidate();
glassPane.repaint();
}
} else {
myHighlightComponent = new HighlightComponent(JBColor.GREEN);
final Point pt = SwingUtilities.convertPoint(c, new Point(0, 0), rootPane);
myHighlightComponent.setBounds(pt.x, pt.y, c.getWidth(), c.getHeight());
glassPane.add(myHighlightComponent);
glassPane.revalidate();
glassPane.repaint();
}
}
}
}
public JComponent getComponent() {
return myComponent;
}
}
private static class ComponentTreeCellRenderer extends JLabel implements TreeCellRenderer {
private ComponentTreeCellRenderer() {
setOpaque(true);
setFont(UIUtil.getLabelFont().deriveFont(11f));
setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
}
@Override
public Component getTreeCellRendererComponent(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
Color foreground = selected ? UIUtil.getTreeSelectionForeground() : UIUtil.getTreeForeground();
Color background = selected ? UIUtil.getTreeSelectionBackground() : null;
if (value instanceof HierarchyTree.ComponentNode) {
HierarchyTree.ComponentNode componentNode = (HierarchyTree.ComponentNode)value;
Component component = componentNode.getOwnComponent();
String name = component.getName();
if (StringUtil.isEmpty(name)) {
name = component.getClass().getSimpleName();
if (name.isEmpty()) {
name = component.getClass().getSuperclass().getSimpleName();
}
}
if (!selected) {
if (!component.isVisible()) {
foreground = JBColor.GRAY;
}
else if (component.getWidth() == 0 || component.getHeight() == 0) {
foreground = new Color(128, 10, 0);
}
else if (component.getPreferredSize() != null &&
(component.getSize().width < component.getPreferredSize().width
|| component.getSize().height < component.getPreferredSize().height)) {
foreground = PlatformColors.BLUE;
}
if (componentNode.getToSelect() == componentNode.getOwnComponent()) {
background = new Color(31, 128, 8, 58);
}
}
setText(name);
}
setForeground(foreground);
setBackground(background);
return this;
}
}
private abstract static class HierarchyTree extends JTree implements TreeSelectionListener {
private final JComponent myComponent;
private HierarchyTree(JComponent c) {
myComponent = c;
setModel(buildModel(c));
setCellRenderer(new ComponentTreeCellRenderer());
getSelectionModel().addTreeSelectionListener(this);
}
public void expandPath() {
TreeUtil.expandAll(this);
int count = getRowCount();
ComponentNode node = new ComponentNode(myComponent, myComponent.getParent());
for (int i = 0; i < count; i++) {
TreePath row = getPathForRow(i);
if (row.getLastPathComponent().equals(node)) {
setSelectionPath(row);
scrollPathToVisible(getSelectionPath());
break;
}
}
}
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getNewLeadSelectionPath();
if (path == null) {
onComponentChanged(null);
return;
}
Object component = path.getLastPathComponent();
if (component instanceof ComponentNode) {
Component c = ((ComponentNode)component).getOwnComponent();
onComponentChanged(c);
}
}
private static TreeModel buildModel(JComponent c) {
return new DefaultTreeModel(new ComponentNode(c, null));
}
public abstract void onComponentChanged(Component c);
private static class ComponentNode extends DefaultMutableTreeNode {
private final Component myParent;
private final Component myToSelect;
private ComponentNode(Object userObject, Component parent) {
super(userObject);
myParent = parent == null ? SwingUtilities.getWindowAncestor((Component) userObject) : parent;
myToSelect = (Component)userObject;
children = prepareChildren(myToSelect, parent);
}
public Component getOwnComponent() {
return myParent;
}
public Component getToSelect() {
return myToSelect;
}
@Override
public String toString() {
return myParent.getClass().getName();
}
@Override
public boolean equals(Object obj) {
return obj instanceof ComponentNode && ((ComponentNode)obj).getOwnComponent() == getOwnComponent();
}
@SuppressWarnings("UseOfObsoleteCollectionType")
private static Vector prepareChildren(Component toSelect, Component parent) {
Vector<ComponentNode> result = new Vector<ComponentNode>();
if (parent == null) {
Container root = SwingUtilities.windowForComponent(toSelect);
for (Component component : root.getComponents()) {
result.add(new ComponentNode(toSelect, component));
}
} else {
if (parent instanceof Container) {
for (Component component : ((Container)parent).getComponents()) {
result.add(new ComponentNode(toSelect, component));
}
}
}
return result;
}
}
}
private static class HighlightComponent extends JComponent {
private Color myColor;
private HighlightComponent(@NotNull final Color c) {
myColor = c;
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
Color oldColor = g2d.getColor();
g2d.setColor(myColor);
Composite old = g2d.getComposite();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
Rectangle r = getBounds();
g2d.fillRect(0, 0, r.width, r.height);
g2d.setColor(myColor.darker());
g2d.drawRect(0, 0, r.width - 1, r.height - 1);
g2d.setComposite(old);
g2d.setColor(oldColor);
}
}
private static class InspectorTable extends JPanel {
private JLabel myTitleLabel;
private InspectorTableModel myModel;
private DimensionsComponent myDimensionComponent;
private InspectorTable(@NotNull final Component component) {
setLayout(new BorderLayout());
myTitleLabel = new JLabel(component.getClass().getCanonicalName(), SwingConstants.CENTER);
myModel = new InspectorTableModel(component);
final StripeTable table = new StripeTable(myModel);
TableColumnModel columnModel = table.getColumnModel();
TableColumn propertyColumn = columnModel.getColumn(0);
propertyColumn.setMinWidth(150);
propertyColumn.setMaxWidth(150);
propertyColumn.setResizable(false);
TableColumn valueColumn = columnModel.getColumn(1);
valueColumn.setMinWidth(200);
valueColumn.setResizable(false);
valueColumn.setCellRenderer(new ValueCellRenderer());
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
add(myTitleLabel, BorderLayout.NORTH);
add(new JBScrollPane(table), BorderLayout.CENTER);
myDimensionComponent = new DimensionsComponent(component);
add(myDimensionComponent, BorderLayout.SOUTH);
}
public void refresh() {
myModel.refresh();
myDimensionComponent.update();
myDimensionComponent.repaint();
}
}
private static class DimensionsComponent extends JComponent {
private Component myComponent;
private int myWidth;
private int myHeight;
private Border myBorder;
private Insets myInsets;
private DimensionsComponent(@NotNull final Component component) {
myComponent = component;
setOpaque(true);
setBackground(JBColor.WHITE);
setBorder(new EmptyBorder(5, 0, 5, 0));
setFont(new JLabel().getFont().deriveFont(Font.PLAIN, 9));
update();
}
public void update() {
myWidth = myComponent.getWidth();
myHeight = myComponent.getHeight();
if (myComponent instanceof JComponent) {
myBorder = ((JComponent)myComponent).getBorder();
myInsets = ((JComponent)myComponent).getInsets();
}
}
@Override
protected void paintComponent(final Graphics g) {
Graphics2D g2d = (Graphics2D)g;
Rectangle bounds = getBounds();
g2d.setColor(getBackground());
Insets insets = getInsets();
g2d.fillRect(insets.left, insets.top, bounds.width - insets.left - insets.right, bounds.height - insets.top - insets.bottom);
g2d.setColor(getForeground());
final String sizeString = String.valueOf(myWidth) + " x " + myHeight;
FontMetrics fm = g2d.getFontMetrics();
int sizeWidth = fm.stringWidth(sizeString);
int fontHeight = fm.getHeight();
g2d.drawString(sizeString, bounds.width / 2 - sizeWidth / 2, bounds.height / 2 + fontHeight / 2);
g2d.setColor(JBColor.GRAY);
int innerX = bounds.width / 2 - sizeWidth / 2 - 20;
int innerY = bounds.height / 2 - fontHeight / 2 - 5;
int innerWidth = sizeWidth + 40;
int innerHeight = fontHeight + 10;
g2d.drawRect(innerX, innerY, innerWidth, innerHeight);
Insets borderInsets = null;
if (myBorder != null) borderInsets = myBorder.getBorderInsets(myComponent);
UIUtil.drawDottedRectangle(g2d, innerX - 15, innerY - 15, innerX - 15 + innerWidth + 30, innerY - 15 + innerHeight + 30);
drawInsets(g2d, fm, "border", borderInsets, 15, fontHeight, innerX, innerY, innerWidth, innerHeight);
g2d.drawRect(innerX - 30, innerY - 30, innerWidth + 60, innerHeight + 60);
drawInsets(g2d, fm, "insets", myInsets, 30, fontHeight, innerX, innerY, innerWidth, innerHeight);
}
private static void drawInsets(Graphics2D g2d, FontMetrics fm, String name, Insets insets, int offset, int fontHeight, int innerX, int innerY, int innerWidth, int innerHeight) {
g2d.setColor(JBColor.BLACK);
g2d.drawString(name, innerX - offset + 5, innerY - offset + fontHeight);
g2d.setColor(JBColor.GRAY);
int dashWidth = fm.stringWidth("-");
if (insets != null) {
final String top = Integer.toString(insets.top);
final String bottom = Integer.toString(insets.bottom);
final String left = Integer.toString(insets.left);
final String right = Integer.toString(insets.right);
g2d.drawString(top, innerX - offset + ((innerWidth + offset * 2) / 2 - fm.stringWidth(top) / 2), innerY - offset + fontHeight);
g2d.drawString(bottom, innerX - offset + ((innerWidth + offset * 2) / 2 - fm.stringWidth(bottom) / 2), innerY - offset + innerHeight + offset*2 - 8 + fontHeight / 2);
g2d.drawString(left, innerX - offset + 7 - fm.stringWidth(left) / 2, innerY - offset + (innerHeight + offset * 2) / 2 + fontHeight / 2);
g2d.drawString(right, innerX + innerWidth + offset - 7 - fm.stringWidth(right) / 2, innerY - offset + (innerHeight + offset * 2) / 2 + fontHeight / 2);
} else {
g2d.drawString("-", innerX - offset + ((innerWidth + offset * 2) / 2 - dashWidth / 2), innerY - offset + fontHeight);
g2d.drawString("-", innerX - offset + ((innerWidth + offset * 2) / 2 - dashWidth / 2), innerY - offset + innerHeight + offset*2 - 8 + fontHeight / 2);
g2d.drawString("-", innerX - offset + 7 - dashWidth / 2, innerY - offset + (innerHeight + offset * 2) / 2 + fontHeight / 2);
g2d.drawString("-", innerX + innerWidth + offset - 7 - dashWidth / 2, innerY - offset + (innerHeight + offset * 2) / 2 + fontHeight / 2);
}
}
@Override
public Dimension getMinimumSize() {
return new Dimension(120, 120);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(150, 150);
}
}
private static class ValueCellRenderer implements TableCellRenderer {
private static final Map<Class, Renderer> RENDERERS = ContainerUtil.newHashMap();
static {
RENDERERS.put(Point.class, new PointRenderer());
RENDERERS.put(Dimension.class, new DimensionRenderer());
RENDERERS.put(Insets.class, new InsetsRenderer());
RENDERERS.put(Rectangle.class, new RectangleRenderer());
RENDERERS.put(Color.class, new ColorRenderer());
RENDERERS.put(Font.class, new FontRenderer());
RENDERERS.put(Boolean.class, new BooleanRenderer());
RENDERERS.put(Icon.class, new IconRenderer());
}
private static final Renderer<Object> DEFAULT_RENDERER = new ObjectRenderer();
private static final JLabel NULL_RENDERER = new JLabel("-");
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
NULL_RENDERER.setOpaque(isSelected);
NULL_RENDERER.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
NULL_RENDERER.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return NULL_RENDERER;
}
Renderer<Object> renderer = ObjectUtils.notNull(getRenderer(value.getClass()), DEFAULT_RENDERER);
JComponent result = renderer.setValue(value);
result.setOpaque(isSelected);
result.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
result.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return result;
}
@Nullable
private static Renderer<Object> getRenderer(Class clazz) {
if (clazz == null) return null;
Renderer<Object> renderer = (Renderer<Object>)RENDERERS.get(clazz);
if (renderer != null) return renderer;
Class[] interfaces = clazz.getInterfaces();
for (Class aClass : interfaces) {
renderer = getRenderer(aClass);
if (renderer != null) {
return renderer;
}
}
clazz = clazz.getSuperclass();
if (clazz != null) {
return getRenderer(clazz);
}
return null;
}
}
private interface Renderer<T> {
JComponent setValue(@NotNull T value);
}
private static class PointRenderer extends JLabel implements Renderer<Point> {
public JComponent setValue(@NotNull final Point value) {
setText(String.valueOf(value.x) + ':' + value.y);
return this;
}
}
private static class DimensionRenderer extends JLabel implements Renderer<Dimension> {
public JComponent setValue(@NotNull final Dimension value) {
setText(String.valueOf(value.width) + " x " + value.height);
return this;
}
}
private static class InsetsRenderer extends JLabel implements Renderer<Insets> {
public JComponent setValue(@NotNull final Insets value) {
setText("top: " + value.top + " left:" + value.left + " bottom:" + value.bottom + " right:" + value.right);
return this;
}
}
private static class RectangleRenderer extends JLabel implements Renderer<Rectangle> {
public JComponent setValue(@NotNull final Rectangle value) {
setText(String.valueOf(value.x) + ":" + value.y + ", " + value.width + " x " + value.height);
return this;
}
}
private static class ColorRenderer extends JLabel implements Renderer<Color> {
public JComponent setValue(@NotNull final Color value) {
setText("r:" + value.getRed() + ", g:" + value.getGreen() + ", b:" + value.getBlue());
setIcon(new ColorIcon(13, 11, value, true));
return this;
}
}
private static class FontRenderer extends JLabel implements Renderer<Font> {
public JComponent setValue(@NotNull final Font value) {
setText(value.getFontName() + " (" + value.getFamily() + "), " + value.getSize() + "px");
return this;
}
}
private static class BooleanRenderer extends JLabel implements Renderer<Boolean> {
public JComponent setValue(@NotNull final Boolean value) {
setText(value ? "Yes" : "No");
return this;
}
}
private static class IconRenderer extends JLabel implements Renderer<Icon> {
public JComponent setValue(@NotNull final Icon value) {
setIcon(value);
return this;
}
}
private static class ObjectRenderer extends JLabel implements Renderer<Object> {
{
putClientProperty("html.disable", Boolean.TRUE);
}
public JComponent setValue(@NotNull final Object value) {
setText(String.valueOf(value).replace('\n', ' '));
return this;
}
}
private static class PropertyBean {
private PropertyBean(String propertyName, Object propertyValue, boolean componentMethod) {
this.propertyName = propertyName;
this.propertyValue = propertyValue;
isComponentMethod = componentMethod;
}
public String propertyName;
public Object propertyValue;
public boolean isComponentMethod;
}
private static class InspectorTableModel extends AbstractTableModel {
private static final String[] JCOMPONENT_METHODS = new String[] {
"getLocation", "getLocationOnScreen",
"getSize", "isOpaque", "getBorder",
"getForeground", "getBackground", "getFont",
"getMinimumSize", "getMaximumSize", "getPreferredSize",
"getAlignmentX", "getAlignmentY",
"getText", "isEditable", "getIcon",
"getTooltipText", "getToolTipText",
"getVisibleRect", "getLayout",
"isFocusCycleRoot", "isValid", "isDisplayable", "isShowing", "isEnabled", "isLightweight", "isFocusable", "isFocusOwner"
};
private Component myComponent;
private List<PropertyBean> myProperties = new ArrayList<PropertyBean>();
public InspectorTableModel(@NotNull final Component c) {
myComponent = c;
fillTable();
}
private void fillTable() {
final Class<? extends Component> cls = myComponent.getClass();
for (final String methodName: JCOMPONENT_METHODS) {
try {
final Method method = cls.getMethod(methodName);
final Object result = method.invoke(myComponent);
final String propertyName = methodName.startsWith("is") ? StringUtil.decapitalize(methodName.substring(2)) : StringUtil.decapitalize(methodName.substring(3));
myProperties.add(new PropertyBean(propertyName, result, true));
}
catch (NoSuchMethodException e) {
// skip
}
catch (InvocationTargetException e) {
// skip
}
catch (IllegalAccessException e) {
// skip
}
}
}
@Nullable
public Object getValueAt(int row, int column) {
final PropertyBean bean = myProperties.get(row);
if (bean != null) {
switch (column) {
case 0:
return bean.propertyName;
default:
return bean.propertyValue;
}
}
return null;
}
public int getColumnCount() {
return 2;
}
public int getRowCount() {
return myProperties.size();
}
public String getColumnName(int columnIndex) {
return columnIndex == 0 ? "Property" : "Value";
}
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? String.class : Object.class;
}
public void refresh() {
myProperties.clear();
fillTable();
fireTableDataChanged();
}
}
private static class UiInspector implements AWTEventListener, Disposable {
private Map<JComponent, InspectorWindow> myComponentToInspector = new HashMap<JComponent, InspectorWindow>();
public void dispose() {
for (final JComponent c : myComponentToInspector.keySet()) {
final InspectorWindow window = myComponentToInspector.get(c);
window.close();
}
myComponentToInspector.clear();
}
public void showInspector(@NotNull final JComponent c) {
InspectorWindow window = myComponentToInspector.get(c);
if (window != null) {
window.setVisible(true);
window.toFront();
} else {
window = new InspectorWindow(c, this);
myComponentToInspector.put(c, window);
window.pack();
window.setVisible(true);
window.toFront();
}
}
public void eventDispatched(final AWTEvent event) {
if (event instanceof MouseEvent) {
final MouseEvent me = (MouseEvent)event;
if (me.isAltDown() && me.isControlDown()) {
switch (me.getID()) {
case MouseEvent.MOUSE_CLICKED:
if (me.getClickCount() == 1 && !me.isPopupTrigger()) {
Object source = me.getSource();
if (source instanceof JComponent) {
showInspector((JComponent) source);
} else {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (owner instanceof JComponent) {
showInspector((JComponent)owner);
}
}
me.consume();
}
break;
default:
break;
}
}
}
}
public void closed(final InspectorWindow inspectorWindow) {
JComponent c = inspectorWindow.getComponent();
myComponentToInspector.remove(c);
}
}
}