From 512ba8f80c0a2422f9e58a1401cb142db190a56c Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Wed, 6 May 2020 12:55:46 +0000 Subject: Revert "Remove unused project." This reverts commit eaada771a6e62ca494a625ce1b060f1a94fa9877. Reason for revert: Breaks build_test and other builds Bug: 155862492 Change-Id: I1a14b84d54b05596ec250b31ae56e437cf49fd81 --- .../com/android/hierarchyviewer/ui/Workspace.java | 1621 ++++++++++++++++++++ 1 file changed, 1621 insertions(+) create mode 100644 hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java (limited to 'hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java') diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java new file mode 100644 index 000000000..bfa15b3d4 --- /dev/null +++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java @@ -0,0 +1,1621 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * 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.android.hierarchyviewer.ui; + +import com.android.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.IDevice; +import com.android.hierarchyviewer.device.DeviceBridge; +import com.android.hierarchyviewer.device.Window; +import com.android.hierarchyviewer.laf.UnifiedContentBorder; +import com.android.hierarchyviewer.scene.CaptureLoader; +import com.android.hierarchyviewer.scene.ProfilesLoader; +import com.android.hierarchyviewer.scene.VersionLoader; +import com.android.hierarchyviewer.scene.ViewHierarchyLoader; +import com.android.hierarchyviewer.scene.ViewHierarchyScene; +import com.android.hierarchyviewer.scene.ViewManager; +import com.android.hierarchyviewer.scene.ViewNode; +import com.android.hierarchyviewer.scene.WindowsLoader; +import com.android.hierarchyviewer.ui.action.CaptureLayersAction; +import com.android.hierarchyviewer.ui.action.CaptureNodeAction; +import com.android.hierarchyviewer.ui.action.DumpDisplayListAction; +import com.android.hierarchyviewer.ui.action.ExitAction; +import com.android.hierarchyviewer.ui.action.InvalidateAction; +import com.android.hierarchyviewer.ui.action.LoadGraphAction; +import com.android.hierarchyviewer.ui.action.RefreshWindowsAction; +import com.android.hierarchyviewer.ui.action.RequestLayoutAction; +import com.android.hierarchyviewer.ui.action.SaveSceneAction; +import com.android.hierarchyviewer.ui.action.ShowDevicesAction; +import com.android.hierarchyviewer.ui.action.StartServerAction; +import com.android.hierarchyviewer.ui.action.StopServerAction; +import com.android.hierarchyviewer.ui.model.ProfilesTableModel; +import com.android.hierarchyviewer.ui.model.PropertiesTableModel; +import com.android.hierarchyviewer.ui.model.ViewsTreeModel; +import com.android.hierarchyviewer.ui.util.IconLoader; +import com.android.hierarchyviewer.ui.util.PngFileFilter; +import com.android.hierarchyviewer.ui.util.PsdFileFilter; +import com.android.hierarchyviewer.util.OS; +import com.android.hierarchyviewer.util.WorkerThread; + +import org.netbeans.api.visual.graph.layout.TreeGraphLayout; +import org.netbeans.api.visual.model.ObjectSceneEvent; +import org.netbeans.api.visual.model.ObjectSceneEventType; +import org.netbeans.api.visual.model.ObjectSceneListener; +import org.netbeans.api.visual.model.ObjectState; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import javax.imageio.ImageIO; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JSplitPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.JTree; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreePath; + +public class Workspace extends JFrame { + private JLabel viewCountLabel; + private JSlider zoomSlider; + private JSplitPane sideSplitter; + private JSplitPane mainSplitter; + private JTable propertiesTable; + private JTable profilingTable; + private JComponent pixelPerfectPanel; + private JTree pixelPerfectTree; + private ScreenViewer screenViewer; + + private JPanel extrasPanel; + private LayoutRenderer layoutView; + + private JScrollPane sceneScroller; + private JComponent sceneView; + + private ViewHierarchyScene scene; + + private ActionMap actionsMap; + private JPanel mainPanel; + private JProgressBar progress; + private JToolBar buttonsPanel; + private JToolBar commandButtonsPanel; + + private JComponent deviceSelector; + private DevicesTableModel devicesTableModel; + private WindowsTableModel windowsTableModel; + + private IDevice currentDevice; + private Window currentWindow = Window.FOCUSED_WINDOW; + + private JButton displayNodeButton; + private JButton dumpDisplayListButton; + private JButton captureLayersButton; + private JButton invalidateButton; + private JButton requestLayoutButton; + private JButton loadButton; + private JButton startButton; + private JButton stopButton; + private JButton showDevicesButton; + private JButton refreshButton; + private JToggleButton graphViewButton; + private JToggleButton pixelPerfectViewButton; + private JMenuItem saveMenuItem; + private JMenuItem showDevicesMenuItem; + private JMenuItem loadMenuItem; + private JMenuItem startMenuItem; + private JMenuItem stopMenuItem; + private JTable devices; + private JTable windows; + private JLabel minZoomLabel; + private JLabel maxZoomLabel; + private JTextField filterText; + private JLabel filterLabel; + + private int protocolVersion; + private int serverVersion; + + public Workspace() { + super("Hierarchy Viewer"); + + buildActions(); + add(buildMainPanel()); + setJMenuBar(buildMenuBar()); + + devices.changeSelection(0, 0, false, false); + currentDeviceChanged(); + + pack(); + } + + private void buildActions() { + actionsMap = new ActionMap(); + actionsMap.put(ExitAction.ACTION_NAME, new ExitAction(this)); + actionsMap.put(ShowDevicesAction.ACTION_NAME, new ShowDevicesAction(this)); + actionsMap.put(LoadGraphAction.ACTION_NAME, new LoadGraphAction(this)); + actionsMap.put(SaveSceneAction.ACTION_NAME, new SaveSceneAction(this)); + actionsMap.put(StartServerAction.ACTION_NAME, new StartServerAction(this)); + actionsMap.put(StopServerAction.ACTION_NAME, new StopServerAction(this)); + actionsMap.put(InvalidateAction.ACTION_NAME, new InvalidateAction(this)); + actionsMap.put(RequestLayoutAction.ACTION_NAME, new RequestLayoutAction(this)); + actionsMap.put(DumpDisplayListAction.ACTION_NAME, new DumpDisplayListAction(this)); + actionsMap.put(CaptureNodeAction.ACTION_NAME, new CaptureNodeAction(this)); + actionsMap.put(CaptureLayersAction.ACTION_NAME, new CaptureLayersAction(this)); + actionsMap.put(RefreshWindowsAction.ACTION_NAME, new RefreshWindowsAction(this)); + } + + private JComponent buildMainPanel() { + mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + commandButtonsPanel = buildToolBar(); + mainPanel.add(commandButtonsPanel, BorderLayout.PAGE_START); + mainPanel.add(deviceSelector = buildDeviceSelector(), BorderLayout.CENTER); + mainPanel.add(buildStatusPanel(), BorderLayout.SOUTH); + + mainPanel.setPreferredSize(new Dimension(1200, 800)); + + return mainPanel; + } + + private JComponent buildGraphPanel() { + sceneScroller = new JScrollPane(); + sceneScroller.setBorder(null); + + mainSplitter = new JSplitPane(); + mainSplitter.setResizeWeight(1.0); + mainSplitter.setContinuousLayout(true); + if (OS.isMacOsX() && OS.isLeopardOrLater()) { + mainSplitter.setBorder(new UnifiedContentBorder()); + } + + mainSplitter.setLeftComponent(sceneScroller); + mainSplitter.setRightComponent(buildSideSplitter()); + + return mainSplitter; + } + + private JComponent buildDeviceSelector() { + JPanel panel = new JPanel(new GridBagLayout()); + if (OS.isMacOsX() && OS.isLeopardOrLater()) { + panel.setBorder(new UnifiedContentBorder()); + } + + devicesTableModel = new DevicesTableModel(); + for (IDevice device : DeviceBridge.getDevices()) { + DeviceBridge.setupDeviceForward(device); + devicesTableModel.addDevice(device); + } + DeviceBridge.startListenForDevices(devicesTableModel); + + devices = new JTable(devicesTableModel); + devices.getSelectionModel().addListSelectionListener(new DeviceSelectedListener()); + devices.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + devices.setBorder(null); + JScrollPane devicesScroller = new JScrollPane(devices); + devicesScroller.setBorder(null); + panel.add(devicesScroller, new GridBagConstraints(0, 0, 1, 1, 0.5, 1.0, + GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), + 0, 0)); + + windowsTableModel = new WindowsTableModel(); + windowsTableModel.setVisible(false); + + windows = new JTable(windowsTableModel); + windows.getSelectionModel().addListSelectionListener(new WindowSelectedListener()); + windows.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + windows.setBorder(null); + JScrollPane windowsScroller = new JScrollPane(windows); + windowsScroller.setBorder(null); + panel.add(windowsScroller, new GridBagConstraints(2, 0, 1, 1, 0.5, 1.0, + GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), + 0, 0)); + + return panel; + } + + private JComponent buildSideSplitter() { + propertiesTable = new JTable(); + propertiesTable.setModel(new DefaultTableModel(new Object[][] { }, + new String[] { "Property", "Value" })); + propertiesTable.setBorder(null); + propertiesTable.getTableHeader().setBorder(null); + + JScrollPane tableScroller = new JScrollPane(propertiesTable); + tableScroller.setBorder(null); + + profilingTable = new JTable(); + profilingTable.setModel(new DefaultTableModel(new Object[][] { + { " " , " " }, { " " , " " }, { " " , " " } }, + new String[] { "Operation", "Duration (ms)" })); + profilingTable.setBorder(null); + profilingTable.getTableHeader().setBorder(null); + + JScrollPane firstTableScroller = new JScrollPane(profilingTable); + firstTableScroller.setBorder(null); + + setVisibleRowCount(profilingTable, 5); + firstTableScroller.setMinimumSize(profilingTable.getPreferredScrollableViewportSize()); + + JSplitPane tablesSplitter = new JSplitPane(); + tablesSplitter.setBorder(null); + tablesSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT); + tablesSplitter.setResizeWeight(0); + tablesSplitter.setLeftComponent(firstTableScroller); + tablesSplitter.setBottomComponent(tableScroller); + tablesSplitter.setContinuousLayout(true); + + sideSplitter = new JSplitPane(); + sideSplitter.setBorder(null); + sideSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT); + sideSplitter.setResizeWeight(0.5); + sideSplitter.setLeftComponent(tablesSplitter); + sideSplitter.setBottomComponent(null); + sideSplitter.setContinuousLayout(true); + + return sideSplitter; + } + + private JPanel buildStatusPanel() { + JPanel statusPanel = new JPanel(); + statusPanel.setLayout(new BorderLayout()); + + JPanel leftSide = new JPanel(); + leftSide.setOpaque(false); + leftSide.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5)); + leftSide.add(Box.createHorizontalStrut(6)); + + ButtonGroup group = new ButtonGroup(); + + graphViewButton = new JToggleButton(IconLoader.load(getClass(), + "/images/icon-graph-view.png")); + graphViewButton.setSelectedIcon(IconLoader.load(getClass(), + "/images/icon-graph-view-selected.png")); + graphViewButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + graphViewButton.putClientProperty("JButton.segmentPosition", "first"); + graphViewButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + toggleGraphView(); + } + }); + group.add(graphViewButton); + leftSide.add(graphViewButton); + + pixelPerfectViewButton = new JToggleButton(IconLoader.load(getClass(), + "/images/icon-pixel-perfect-view.png")); + pixelPerfectViewButton.setSelectedIcon(IconLoader.load(getClass(), + "/images/icon-pixel-perfect-view-selected.png")); + pixelPerfectViewButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + pixelPerfectViewButton.putClientProperty("JButton.segmentPosition", "last"); + pixelPerfectViewButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + togglePixelPerfectView(); + } + }); + group.add(pixelPerfectViewButton); + leftSide.add(pixelPerfectViewButton); + + graphViewButton.setSelected(true); + + filterText = new JTextField(20); + filterText.putClientProperty("JComponent.sizeVariant", "small"); + filterText.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { + updateFilter(e); + } + + public void removeUpdate(DocumentEvent e) { + updateFilter(e); + } + + public void changedUpdate(DocumentEvent e) { + updateFilter(e); + } + }); + + filterLabel = new JLabel("Filter by class or id:"); + filterLabel.putClientProperty("JComponent.sizeVariant", "small"); + filterLabel.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 6)); + + leftSide.add(filterLabel); + leftSide.add(filterText); + + minZoomLabel = new JLabel(); + minZoomLabel.setText("20%"); + minZoomLabel.putClientProperty("JComponent.sizeVariant", "small"); + minZoomLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0)); + leftSide.add(minZoomLabel); + + zoomSlider = new JSlider(); + zoomSlider.putClientProperty("JComponent.sizeVariant", "small"); + zoomSlider.setMaximum(200); + zoomSlider.setMinimum(20); + zoomSlider.setValue(100); + zoomSlider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent evt) { + zoomSliderStateChanged(evt); + } + }); + leftSide.add(zoomSlider); + + maxZoomLabel = new JLabel(); + maxZoomLabel.putClientProperty("JComponent.sizeVariant", "small"); + maxZoomLabel.setText("200%"); + leftSide.add(maxZoomLabel); + + viewCountLabel = new JLabel(); + viewCountLabel.setText("0 views"); + viewCountLabel.putClientProperty("JComponent.sizeVariant", "small"); + viewCountLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0)); + leftSide.add(viewCountLabel); + + statusPanel.add(leftSide, BorderLayout.LINE_START); + + JPanel rightSide = new JPanel(); + rightSide.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 12)); + rightSide.setLayout(new FlowLayout(FlowLayout.RIGHT)); + + progress = new JProgressBar(); + progress.setVisible(false); + progress.setIndeterminate(true); + progress.putClientProperty("JComponent.sizeVariant", "mini"); + progress.putClientProperty("JProgressBar.style", "circular"); + rightSide.add(progress); + + statusPanel.add(rightSide, BorderLayout.LINE_END); + + hideStatusBarComponents(); + + return statusPanel; + } + + private void hideStatusBarComponents() { + viewCountLabel.setVisible(false); + zoomSlider.setVisible(false); + minZoomLabel.setVisible(false); + maxZoomLabel.setVisible(false); + filterLabel.setVisible(false); + filterText.setVisible(false); + } + + private JToolBar buildToolBar() { + JToolBar toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); + + startButton = new JButton(); + startButton.setAction(actionsMap.get(StartServerAction.ACTION_NAME)); + startButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + startButton.putClientProperty("JButton.segmentPosition", "first"); + toolBar.add(startButton); + + stopButton = new JButton(); + stopButton.setAction(actionsMap.get(StopServerAction.ACTION_NAME)); + stopButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + stopButton.putClientProperty("JButton.segmentPosition", "middle"); + toolBar.add(stopButton); + + refreshButton = new JButton(); + refreshButton.setAction(actionsMap.get(RefreshWindowsAction.ACTION_NAME)); + refreshButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + refreshButton.putClientProperty("JButton.segmentPosition", "last"); + toolBar.add(refreshButton); + + showDevicesButton = new JButton(); + showDevicesButton.setAction(actionsMap.get(ShowDevicesAction.ACTION_NAME)); + showDevicesButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + showDevicesButton.putClientProperty("JButton.segmentPosition", "first"); + toolBar.add(showDevicesButton); + showDevicesButton.setEnabled(false); + + loadButton = new JButton(); + loadButton.setAction(actionsMap.get(LoadGraphAction.ACTION_NAME)); + loadButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + loadButton.putClientProperty("JButton.segmentPosition", "last"); + toolBar.add(loadButton); + + displayNodeButton = new JButton(); + displayNodeButton.setAction(actionsMap.get(CaptureNodeAction.ACTION_NAME)); + displayNodeButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + displayNodeButton.putClientProperty("JButton.segmentPosition", "first"); + toolBar.add(displayNodeButton); + + dumpDisplayListButton = new JButton(); + dumpDisplayListButton.setAction(actionsMap.get(DumpDisplayListAction.ACTION_NAME)); + dumpDisplayListButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + dumpDisplayListButton.putClientProperty("JButton.segmentPosition", "middle"); + + captureLayersButton = new JButton(); + captureLayersButton.setAction(actionsMap.get(CaptureLayersAction.ACTION_NAME)); + captureLayersButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + captureLayersButton.putClientProperty("JButton.segmentPosition", "middle"); + toolBar.add(captureLayersButton); + + invalidateButton = new JButton(); + invalidateButton.setAction(actionsMap.get(InvalidateAction.ACTION_NAME)); + invalidateButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + invalidateButton.putClientProperty("JButton.segmentPosition", "middle"); + toolBar.add(invalidateButton); + + requestLayoutButton = new JButton(); + requestLayoutButton.setAction(actionsMap.get(RequestLayoutAction.ACTION_NAME)); + requestLayoutButton.putClientProperty("JButton.buttonType", "segmentedTextured"); + requestLayoutButton.putClientProperty("JButton.segmentPosition", "last"); + toolBar.add(requestLayoutButton); + + return toolBar; + } + + private void setupProtocolDependentToolbar() { + // Some functionality is only enabled in certain versions of the protocol. + // Add/remove those buttons here + if (protocolVersion < 4) { + commandButtonsPanel.remove(dumpDisplayListButton); + } else if (dumpDisplayListButton.getParent() == null) { + commandButtonsPanel.add(dumpDisplayListButton, + commandButtonsPanel.getComponentCount() - 1); + } + } + + private JMenuBar buildMenuBar() { + JMenuBar menuBar = new JMenuBar(); + + JMenu fileMenu = new JMenu(); + JMenu viewMenu = new JMenu(); + JMenu viewHierarchyMenu = new JMenu(); + JMenu serverMenu = new JMenu(); + + saveMenuItem = new JMenuItem(); + JMenuItem exitMenuItem = new JMenuItem(); + + showDevicesMenuItem = new JMenuItem(); + + loadMenuItem = new JMenuItem(); + + startMenuItem = new JMenuItem(); + stopMenuItem = new JMenuItem(); + + fileMenu.setText("File"); + + saveMenuItem.setAction(actionsMap.get(SaveSceneAction.ACTION_NAME)); + fileMenu.add(saveMenuItem); + + exitMenuItem.setAction(actionsMap.get(ExitAction.ACTION_NAME)); + fileMenu.add(exitMenuItem); + + menuBar.add(fileMenu); + + viewMenu.setText("View"); + + showDevicesMenuItem.setAction(actionsMap.get(ShowDevicesAction.ACTION_NAME)); + showDevicesMenuItem.setEnabled(false); + viewMenu.add(showDevicesMenuItem); + + menuBar.add(viewMenu); + + viewHierarchyMenu.setText("Hierarchy"); + + loadMenuItem.setAction(actionsMap.get(LoadGraphAction.ACTION_NAME)); + viewHierarchyMenu.add(loadMenuItem); + + menuBar.add(viewHierarchyMenu); + + serverMenu.setText("Server"); + + startMenuItem.setAction(actionsMap.get(StartServerAction.ACTION_NAME)); + serverMenu.add(startMenuItem); + + stopMenuItem.setAction(actionsMap.get(StopServerAction.ACTION_NAME)); + serverMenu.add(stopMenuItem); + + menuBar.add(serverMenu); + + return menuBar; + } + + private JComponent buildPixelPerfectPanel() { + JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + + pixelPerfectTree = new JTree(new Object[0]); + pixelPerfectTree.setBorder(null); + pixelPerfectTree.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); + pixelPerfectTree.addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent event) { + ViewNode node = (ViewNode) event.getPath().getLastPathComponent(); + screenViewer.select(node); + } + }); + + JScrollPane scroller = new JScrollPane(pixelPerfectTree); + scroller.setBorder(null); + scroller.getViewport().setBorder(null); + + splitter.setContinuousLayout(true); + splitter.setLeftComponent(scroller); + splitter.setRightComponent(buildPixelPerfectViewer(splitter)); + splitter.setBorder(null); + + if (OS.isMacOsX() && OS.isLeopardOrLater()) { + splitter.setBorder(new UnifiedContentBorder()); + } + + return splitter; + } + + private JComponent buildPixelPerfectViewer(JSplitPane splitter) { + screenViewer = new ScreenViewer(this, currentDevice, splitter.getDividerSize()); + return screenViewer; + } + + private void toggleGraphView() { + showStatusBarComponents(); + + screenViewer.stop(); + mainPanel.remove(pixelPerfectPanel); + mainPanel.add(mainSplitter, BorderLayout.CENTER); + + validate(); + repaint(); + } + + private void showStatusBarComponents() { + viewCountLabel.setVisible(true); + zoomSlider.setVisible(true); + minZoomLabel.setVisible(true); + maxZoomLabel.setVisible(true); + filterLabel.setVisible(true); + filterText.setVisible(true); + } + + private void togglePixelPerfectView() { + if (pixelPerfectPanel == null) { + pixelPerfectPanel = buildPixelPerfectPanel(); + showPixelPerfectTree(); + } else { + screenViewer.start(); + } + + hideStatusBarComponents(); + + mainPanel.remove(mainSplitter); + mainPanel.add(pixelPerfectPanel, BorderLayout.CENTER); + + validate(); + repaint(); + } + + private void zoomSliderStateChanged(ChangeEvent evt) { + JSlider slider = (JSlider) evt.getSource(); + if (sceneView != null) { + scene.setZoomFactor(slider.getValue() / 100.0d); + sceneView.repaint(); + } + } + + private void showProperties(ViewNode node) { + propertiesTable.setModel(new PropertiesTableModel(node)); + } + + private void updateProfiles(double[] profiles) { + profilingTable.setModel(new ProfilesTableModel(profiles)); + setVisibleRowCount(profilingTable, profiles.length + 1); + } + + public static void setVisibleRowCount(JTable table, int rows) { + int height = 0; + for (int row = 0; row < rows; row++) { + height += table.getRowHeight(row); + } + + Dimension size = new Dimension(table.getPreferredScrollableViewportSize().width, height); + table.setPreferredScrollableViewportSize(size); + table.revalidate(); + } + + private void showPixelPerfectTree() { + if (pixelPerfectTree == null) { + return; + } + pixelPerfectTree.setModel(new ViewsTreeModel(scene.getRoot())); + pixelPerfectTree.setCellRenderer(new ViewsTreeCellRenderer()); + expandAll(pixelPerfectTree, true); + + } + + private static void expandAll(JTree tree, boolean expand) { + ViewNode root = (ViewNode) tree.getModel().getRoot(); + expandAll(tree, new TreePath(root), expand); + } + + private static void expandAll(JTree tree, TreePath parent, boolean expand) { + // Traverse children + ViewNode node = (ViewNode)parent.getLastPathComponent(); + if (node.children != null) { + for (ViewNode n : node.children) { + TreePath path = parent.pathByAddingChild(n); + expandAll(tree, path, expand); + } + } + + if (expand) { + tree.expandPath(parent); + } else { + tree.collapsePath(parent); + } + } + + private void createGraph(ViewHierarchyScene scene) { + scene.addObjectSceneListener(new SceneFocusListener(), + ObjectSceneEventType.OBJECT_FOCUS_CHANGED); + + if (mainSplitter == null) { + mainPanel.remove(deviceSelector); + mainPanel.add(buildGraphPanel(), BorderLayout.CENTER); + showDevicesButton.setEnabled(true); + showDevicesMenuItem.setEnabled(true); + graphViewButton.setEnabled(true); + pixelPerfectViewButton.setEnabled(true); + + showStatusBarComponents(); + } + + sceneView = scene.createView(); + sceneView.addMouseListener(new NodeClickListener()); + sceneView.addMouseWheelListener(new WheelZoomListener()); + sceneScroller.setViewportView(sceneView); + + if (extrasPanel != null) { + sideSplitter.remove(extrasPanel); + } + sideSplitter.setBottomComponent(buildExtrasPanel()); + + mainSplitter.setDividerLocation(getWidth() - mainSplitter.getDividerSize() - + buttonsPanel.getPreferredSize().width); + + captureLayersButton.setEnabled(true); + saveMenuItem.setEnabled(true); + showPixelPerfectTree(); + + updateStatus(); + layoutScene(); + } + + private void layoutScene() { + TreeGraphLayout layout = + new TreeGraphLayout(scene, 50, 50, 70, 30, true); + layout.layout(scene.getRoot()); + } + + private void updateStatus() { + viewCountLabel.setText("" + scene.getNodes().size() + " views"); + zoomSlider.setEnabled(scene.getNodes().size() > 0); + } + + private JPanel buildExtrasPanel() { + extrasPanel = new JPanel(new BorderLayout()); + JScrollPane p = new JScrollPane(layoutView = new LayoutRenderer(scene, sceneView)); + JScrollBar b = p.getVerticalScrollBar(); + b.setUnitIncrement(10); + extrasPanel.add(p); + extrasPanel.add(scene.createSatelliteView(), BorderLayout.SOUTH); + extrasPanel.add(buildLayoutViewControlButtons(), BorderLayout.NORTH); + return extrasPanel; + } + + private JComponent buildLayoutViewControlButtons() { + buttonsPanel = new JToolBar(); + buttonsPanel.setFloatable(false); + + ButtonGroup group = new ButtonGroup(); + + JToggleButton white = new JToggleButton("On White"); + toggleColorOnSelect(white); + white.putClientProperty("JButton.buttonType", "segmentedTextured"); + white.putClientProperty("JButton.segmentPosition", "first"); + white.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + layoutView.setBackground(Color.WHITE); + layoutView.setForeground(Color.BLACK); + } + }); + group.add(white); + buttonsPanel.add(white); + + JToggleButton black = new JToggleButton("On Black"); + toggleColorOnSelect(black); + black.putClientProperty("JButton.buttonType", "segmentedTextured"); + black.putClientProperty("JButton.segmentPosition", "last"); + black.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + layoutView.setBackground(Color.BLACK); + layoutView.setForeground(Color.WHITE); + } + }); + group.add(black); + buttonsPanel.add(black); + + black.setSelected(true); + + JCheckBox showExtras = new JCheckBox("Show Extras"); + showExtras.putClientProperty("JComponent.sizeVariant", "small"); + showExtras.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + layoutView.setShowExtras(((JCheckBox) e.getSource()).isSelected()); + } + }); + buttonsPanel.add(showExtras); + + return buttonsPanel; + } + + private void showCaptureWindow(ViewNode node, String captureParams, Image image) { + if (image != null) { + layoutView.repaint(); + + JFrame frame = new JFrame(captureParams); + JPanel panel = new JPanel(new BorderLayout()); + + final CaptureRenderer label = new CaptureRenderer(new ImageIcon(image), node); + label.setBorder(BorderFactory.createEmptyBorder(24, 24, 24, 24)); + + final JPanel solidColor = new JPanel(new BorderLayout()); + solidColor.setBackground(Color.BLACK); + solidColor.add(label); + + JToolBar toolBar = new JToolBar(); + toolBar.setFloatable(false); + + ButtonGroup group = new ButtonGroup(); + + JToggleButton white = new JToggleButton("On White"); + toggleColorOnSelect(white); + white.putClientProperty("JButton.buttonType", "segmentedTextured"); + white.putClientProperty("JButton.segmentPosition", "first"); + white.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + solidColor.setBackground(Color.WHITE); + } + }); + group.add(white); + toolBar.add(white); + + JToggleButton black = new JToggleButton("On Black"); + toggleColorOnSelect(black); + black.putClientProperty("JButton.buttonType", "segmentedTextured"); + black.putClientProperty("JButton.segmentPosition", "last"); + black.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + solidColor.setBackground(Color.BLACK); + } + }); + group.add(black); + toolBar.add(black); + + black.setSelected(true); + + JCheckBox showExtras = new JCheckBox("Show Extras"); + showExtras.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + label.setShowExtras(((JCheckBox) e.getSource()).isSelected()); + } + }); + toolBar.add(showExtras); + + panel.add(toolBar, BorderLayout.NORTH); + panel.add(solidColor); + frame.add(panel); + + frame.pack(); + frame.setResizable(false); + frame.setLocationRelativeTo(Workspace.this); + frame.setVisible(true); + } + } + + private void reset() { + currentDevice = null; + currentWindow = null; + currentDeviceChanged(); + windowsTableModel.setVisible(false); + windowsTableModel.clear(); + + showDevicesSelector(); + } + + public void showDevicesSelector() { + if (mainSplitter != null) { + if (pixelPerfectPanel != null) { + screenViewer.start(); + } + mainPanel.remove(graphViewButton.isSelected() ? mainSplitter : pixelPerfectPanel); + mainPanel.add(deviceSelector, BorderLayout.CENTER); + pixelPerfectPanel = mainSplitter = null; + graphViewButton.setSelected(true); + + hideStatusBarComponents(); + + saveMenuItem.setEnabled(false); + showDevicesMenuItem.setEnabled(false); + showDevicesButton.setEnabled(false); + displayNodeButton.setEnabled(false); + captureLayersButton.setEnabled(false); + invalidateButton.setEnabled(false); + dumpDisplayListButton.setEnabled(false); + requestLayoutButton.setEnabled(false); + graphViewButton.setEnabled(false); + pixelPerfectViewButton.setEnabled(false); + + if (currentDevice != null) { + if (!DeviceBridge.isViewServerRunning(currentDevice)) { + DeviceBridge.startViewServer(currentDevice); + } + loadWindows().execute(); + windowsTableModel.setVisible(true); + } + + validate(); + repaint(); + } + } + + private void currentDeviceChanged() { + if (currentDevice == null) { + startButton.setEnabled(false); + startMenuItem.setEnabled(false); + stopButton.setEnabled(false); + stopMenuItem.setEnabled(false); + refreshButton.setEnabled(false); + saveMenuItem.setEnabled(false); + loadButton.setEnabled(false); + displayNodeButton.setEnabled(false); + captureLayersButton.setEnabled(false); + invalidateButton.setEnabled(false); + dumpDisplayListButton.setEnabled(false); + graphViewButton.setEnabled(false); + pixelPerfectViewButton.setEnabled(false); + requestLayoutButton.setEnabled(false); + loadMenuItem.setEnabled(false); + } else { + loadMenuItem.setEnabled(true); + checkForServerOnCurrentDevice(); + } + } + + private void checkForServerOnCurrentDevice() { + if (DeviceBridge.isViewServerRunning(currentDevice)) { + startButton.setEnabled(false); + startMenuItem.setEnabled(false); + stopButton.setEnabled(true); + stopMenuItem.setEnabled(true); + loadButton.setEnabled(true); + refreshButton.setEnabled(true); + } else { + startButton.setEnabled(true); + startMenuItem.setEnabled(true); + stopButton.setEnabled(false); + stopMenuItem.setEnabled(false); + loadButton.setEnabled(false); + refreshButton.setEnabled(false); + } + } + + public void cleanupDevices() { + for (IDevice device : devicesTableModel.getDevices()) { + DeviceBridge.removeDeviceForward(device); + } + } + + private static void toggleColorOnSelect(JToggleButton button) { + if (!OS.isMacOsX() || !OS.isLeopardOrLater()) { + return; + } + + button.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent event) { + JToggleButton button = (JToggleButton) event.getSource(); + if (button.isSelected()) { + button.setForeground(Color.WHITE); + } else { + button.setForeground(Color.BLACK); + } + } + }); + } + + private void updateFilter(DocumentEvent e) { + final Document document = e.getDocument(); + try { + updateFilteredNodes(document.getText(0, document.getLength())); + } catch (BadLocationException e1) { + e1.printStackTrace(); + } + } + + private void updateFilteredNodes(String filterText) { + final ViewNode root = scene.getRoot(); + try { + final Pattern pattern = Pattern.compile(filterText, Pattern.CASE_INSENSITIVE); + filterNodes(pattern, root); + } catch (PatternSyntaxException e) { + filterNodes(null, root); + } + repaint(); + } + + private void filterNodes(Pattern pattern, ViewNode root) { + root.filter(pattern); + + for (ViewNode node : root.children) { + filterNodes(pattern, node); + } + } + + public void beginTask() { + progress.setVisible(true); + } + + public void endTask() { + progress.setVisible(false); + } + + public SwingWorker showNodeCapture() { + if (scene.getFocusedObject() == null) { + return null; + } + return new CaptureNodeTask(); + } + + public SwingWorker outputDisplayList() { + if (scene.getFocusedObject() == null) { + return null; + } + return new DumpDisplayListTask(); + } + + public SwingWorker captureLayers() { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new PsdFileFilter()); + int choice = chooser.showSaveDialog(sceneView); + if (choice == JFileChooser.APPROVE_OPTION) { + return new CaptureLayersTask(chooser.getSelectedFile()); + } else { + return null; + } + } + + public SwingWorker startServer() { + return new StartServerTask(); + } + + public SwingWorker stopServer() { + return new StopServerTask(); + } + + public SwingWorker loadWindows() { + return new LoadWindowsTask(); + } + + public SwingWorker loadGraph() { + return new LoadGraphTask(); + } + + public SwingWorker invalidateView() { + if (scene.getFocusedObject() == null) { + return null; + } + return new InvalidateTask(); + } + + public SwingWorker requestLayout() { + if (scene.getFocusedObject() == null) { + return null; + } + return new RequestLayoutTask(); + } + + public SwingWorker saveSceneAsImage() { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new PngFileFilter()); + int choice = chooser.showSaveDialog(sceneView); + if (choice == JFileChooser.APPROVE_OPTION) { + return new SaveSceneTask(chooser.getSelectedFile()); + } else { + return null; + } + } + + private class InvalidateTask extends SwingWorker { + private String captureParams; + + private InvalidateTask() { + captureParams = scene.getFocusedObject().toString(); + beginTask(); + } + + @Override + @WorkerThread + protected Object doInBackground() throws Exception { + ViewManager.invalidate(currentDevice, currentWindow, captureParams); + return null; + } + + @Override + protected void done() { + endTask(); + } + } + + private class DumpDisplayListTask extends SwingWorker { + private String captureParams; + + private DumpDisplayListTask() { + captureParams = scene.getFocusedObject().toString(); + beginTask(); + } + + @Override + @WorkerThread + protected Object doInBackground() throws Exception { + ViewManager.outputDisplayList(currentDevice, currentWindow, captureParams); + return null; + } + + @Override + protected void done() { + endTask(); + } + } + + private class RequestLayoutTask extends SwingWorker { + private String captureParams; + + private RequestLayoutTask() { + captureParams = scene.getFocusedObject().toString(); + beginTask(); + } + + @Override + @WorkerThread + protected Object doInBackground() throws Exception { + ViewManager.requestLayout(currentDevice, currentWindow, captureParams); + return null; + } + + @Override + protected void done() { + endTask(); + } + } + + private class CaptureLayersTask extends SwingWorker { + private File file; + + private CaptureLayersTask(File file) { + this.file = file; + beginTask(); + } + + @Override + @WorkerThread + protected Boolean doInBackground() throws Exception { + return CaptureLoader.saveLayers(currentDevice, currentWindow, file); + } + + @Override + protected void done() { + endTask(); + } + } + + private class CaptureNodeTask extends SwingWorker { + private String captureParams; + private ViewNode node; + + private CaptureNodeTask() { + node = (ViewNode) scene.getFocusedObject(); + captureParams = node.toString(); + beginTask(); + } + + @Override + @WorkerThread + protected Image doInBackground() throws Exception { + node.image = CaptureLoader.loadCapture(currentDevice, currentWindow, captureParams); + return node.image; + } + + @Override + protected void done() { + try { + Image image = get(); + showCaptureWindow(node, captureParams, image); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } finally { + endTask(); + } + } + } + + static class WindowsResult { + Window[] windows; + int serverVersion; + int protocolVersion; + } + + private class LoadWindowsTask extends SwingWorker { + private LoadWindowsTask() { + beginTask(); + } + + @Override + @WorkerThread + protected WindowsResult doInBackground() throws Exception { + WindowsResult r = new WindowsResult(); + r.protocolVersion = VersionLoader.loadProtocolVersion(currentDevice); + r.serverVersion = VersionLoader.loadServerVersion(currentDevice); + r.windows = WindowsLoader.loadWindows(currentDevice, + r.protocolVersion, r.serverVersion); + return r; + } + + @Override + protected void done() { + try { + WindowsResult result = get(); + protocolVersion = result.protocolVersion; + serverVersion = result.serverVersion; + setupProtocolDependentToolbar(); + windowsTableModel.clear(); + windowsTableModel.addWindows(result.windows); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + endTask(); + } + } + } + + private class StartServerTask extends SwingWorker { + public StartServerTask() { + beginTask(); + } + + @Override + @WorkerThread + protected Object doInBackground() { + DeviceBridge.startViewServer(currentDevice); + return null; + } + + @Override + protected void done() { + new LoadWindowsTask().execute(); + windowsTableModel.setVisible(true); + checkForServerOnCurrentDevice(); + endTask(); + } + } + + private class StopServerTask extends SwingWorker { + public StopServerTask() { + beginTask(); + } + + @Override + @WorkerThread + protected Object doInBackground() { + DeviceBridge.stopViewServer(currentDevice); + return null; + } + + @Override + protected void done() { + windowsTableModel.setVisible(false); + windowsTableModel.clear(); + checkForServerOnCurrentDevice(); + endTask(); + } + } + + private class LoadGraphTask extends SwingWorker { + public LoadGraphTask() { + beginTask(); + } + + @Override + @WorkerThread + protected double[] doInBackground() { + scene = ViewHierarchyLoader.loadScene(currentDevice, currentWindow); + return ProfilesLoader.loadProfiles(currentDevice, currentWindow, + scene.getRoot().toString()); + } + + @Override + protected void done() { + try { + createGraph(scene); + updateProfiles(get()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } finally { + endTask(); + } + } + } + + private class SaveSceneTask extends SwingWorker { + private File file; + + private SaveSceneTask(File file) { + this.file = file; + beginTask(); + } + + @Override + @WorkerThread + protected Object doInBackground() { + if (sceneView == null) { + return null; + } + + try { + BufferedImage image = new BufferedImage(sceneView.getWidth(), + sceneView.getHeight(), BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = image.createGraphics(); + sceneView.paint(g2); + g2.dispose(); + ImageIO.write(image, "PNG", file); + } catch (IOException ex) { + ex.printStackTrace(); + } + return null; + } + + @Override + protected void done() { + endTask(); + } + } + + private class SceneFocusListener implements ObjectSceneListener { + + public void objectAdded(ObjectSceneEvent arg0, Object arg1) { + } + + public void objectRemoved(ObjectSceneEvent arg0, Object arg1) { + } + + public void objectStateChanged(ObjectSceneEvent arg0, Object arg1, + ObjectState arg2, ObjectState arg3) { + } + + public void selectionChanged(ObjectSceneEvent e, Set previousSelection, + Set newSelection) { + } + + public void highlightingChanged(ObjectSceneEvent arg0, Set arg1, Set arg2) { + } + + public void hoverChanged(ObjectSceneEvent arg0, Object arg1, Object arg2) { + } + + public void focusChanged(ObjectSceneEvent e, Object oldFocus, Object newFocus) { + displayNodeButton.setEnabled(true); + invalidateButton.setEnabled(true); + dumpDisplayListButton.setEnabled(true); + requestLayoutButton.setEnabled(true); + + Set selection = new HashSet(); + selection.add(newFocus); + scene.setSelectedObjects(selection); + + showProperties((ViewNode) newFocus); + layoutView.repaint(); + } + } + + private class NodeClickListener extends MouseAdapter { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + showNodeCapture().execute(); + } + } + } + + private class WheelZoomListener implements MouseWheelListener { + public void mouseWheelMoved(MouseWheelEvent e) { + if (zoomSlider != null) { + int val = zoomSlider.getValue(); + val -= e.getWheelRotation() * 10; + zoomSlider.setValue(val); + } + } + } + private class DevicesTableModel extends DefaultTableModel implements + AndroidDebugBridge.IDeviceChangeListener { + + private ArrayList devices; + + private DevicesTableModel() { + devices = new ArrayList(); + } + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + @Override + public Object getValueAt(int row, int column) { + return devices.get(row); + } + + @Override + public String getColumnName(int column) { + return "Devices"; + } + + @WorkerThread + public void deviceConnected(final IDevice device) { + DeviceBridge.setupDeviceForward(device); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + addDevice(device); + } + }); + } + + @WorkerThread + public void deviceDisconnected(final IDevice device) { + DeviceBridge.removeDeviceForward(device); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + removeDevice(device); + } + }); + } + + public void addDevice(IDevice device) { + if (!devices.contains(device)) { + devices.add(device); + fireTableDataChanged(); + } + } + + public void removeDevice(IDevice device) { + if (device.equals(currentDevice)) { + reset(); + } + + if (devices.contains(device)) { + devices.remove(device); + fireTableDataChanged(); + } + } + + @WorkerThread + public void deviceChanged(IDevice device, int changeMask) { + if ((changeMask & IDevice.CHANGE_STATE) != 0 && + device.isOnline()) { + // if the device state changed and it's now online, we set up its port forwarding. + DeviceBridge.setupDeviceForward(device); + } else if (device == currentDevice && (changeMask & IDevice.CHANGE_CLIENT_LIST) != 0) { + // if the changed device is the current one and the client list changed, we update + // the UI. + loadWindows().execute(); + windowsTableModel.setVisible(true); + } + } + + @Override + public int getRowCount() { + return devices == null ? 0 : devices.size(); + } + + public IDevice getDevice(int index) { + return index < devices.size() ? devices.get(index) : null; + } + + public IDevice[] getDevices() { + return devices.toArray(new IDevice[devices.size()]); + } + } + + private static class WindowsTableModel extends DefaultTableModel { + private ArrayList windows; + private boolean visible; + + private WindowsTableModel() { + windows = new ArrayList(); + windows.add(Window.FOCUSED_WINDOW); + } + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + @Override + public String getColumnName(int column) { + return "Windows"; + } + + @Override + public Object getValueAt(int row, int column) { + return windows.get(row); + } + + @Override + public int getRowCount() { + return !visible || windows == null ? 0 : windows.size(); + } + + public void setVisible(boolean visible) { + this.visible = visible; + fireTableDataChanged(); + } + + public void addWindow(Window window) { + windows.add(window); + fireTableDataChanged(); + } + + public void addWindows(Window[] windowsList) { + //noinspection ManualArrayToCollectionCopy + for (Window window : windowsList) { + windows.add(window); + } + fireTableDataChanged(); + } + + public void clear() { + windows.clear(); + windows.add(Window.FOCUSED_WINDOW); + } + + public Window getWindow(int index) { + return windows.get(index); + } + } + + private class DeviceSelectedListener implements ListSelectionListener { + public void valueChanged(ListSelectionEvent event) { + if (event.getValueIsAdjusting()) { + return; + } + + int row = devices.getSelectedRow(); + if (row >= 0) { + currentDevice = devicesTableModel.getDevice(row); + currentDeviceChanged(); + if (currentDevice != null) { + if (!DeviceBridge.isViewServerRunning(currentDevice)) { + DeviceBridge.startViewServer(currentDevice); + checkForServerOnCurrentDevice(); + } + loadWindows().execute(); + windowsTableModel.setVisible(true); + } + } else { + currentDevice = null; + currentDeviceChanged(); + windowsTableModel.setVisible(false); + windowsTableModel.clear(); + } + } + } + + private class WindowSelectedListener implements ListSelectionListener { + public void valueChanged(ListSelectionEvent event) { + if (event.getValueIsAdjusting()) { + return; + } + + int row = windows.getSelectedRow(); + if (row >= 0) { + currentWindow = windowsTableModel.getWindow(row); + } else { + currentWindow = Window.FOCUSED_WINDOW; + } + } + } + + private static class ViewsTreeCellRenderer extends DefaultTreeCellRenderer { + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, + boolean expanded, boolean leaf, int row, boolean hasFocus) { + + final String name = ((ViewNode) value).name; + value = name.substring(name.lastIndexOf('.') + 1, name.lastIndexOf('@')); + return super.getTreeCellRendererComponent(tree, value, selected, expanded, + leaf, row, hasFocus); + } + } +} -- cgit v1.2.3