Home | History | Annotate | Download | only in hierarchyviewer
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.hierarchyviewer;
     18 
     19 import com.android.ddmlib.Log;
     20 import com.android.ddmuilib.ImageLoader;
     21 import com.android.hierarchyviewer.actions.AboutAction;
     22 import com.android.hierarchyviewer.actions.LoadAllViewsAction;
     23 import com.android.hierarchyviewer.actions.QuitAction;
     24 import com.android.hierarchyviewer.actions.ShowOverlayAction;
     25 import com.android.hierarchyviewer.util.ActionButton;
     26 import com.android.hierarchyviewerlib.HierarchyViewerDirector;
     27 import com.android.hierarchyviewerlib.actions.CapturePSDAction;
     28 import com.android.hierarchyviewerlib.actions.DisplayViewAction;
     29 import com.android.hierarchyviewerlib.actions.DumpDisplayListAction;
     30 import com.android.hierarchyviewerlib.actions.InspectScreenshotAction;
     31 import com.android.hierarchyviewerlib.actions.InvalidateAction;
     32 import com.android.hierarchyviewerlib.actions.LoadOverlayAction;
     33 import com.android.hierarchyviewerlib.actions.LoadViewHierarchyAction;
     34 import com.android.hierarchyviewerlib.actions.PixelPerfectAutoRefreshAction;
     35 import com.android.hierarchyviewerlib.actions.RefreshPixelPerfectAction;
     36 import com.android.hierarchyviewerlib.actions.RefreshPixelPerfectTreeAction;
     37 import com.android.hierarchyviewerlib.actions.RefreshViewAction;
     38 import com.android.hierarchyviewerlib.actions.RefreshWindowsAction;
     39 import com.android.hierarchyviewerlib.actions.RequestLayoutAction;
     40 import com.android.hierarchyviewerlib.actions.SavePixelPerfectAction;
     41 import com.android.hierarchyviewerlib.actions.SaveTreeViewAction;
     42 import com.android.hierarchyviewerlib.device.DeviceBridge.ViewServerInfo;
     43 import com.android.hierarchyviewerlib.models.DeviceSelectionModel;
     44 import com.android.hierarchyviewerlib.models.PixelPerfectModel;
     45 import com.android.hierarchyviewerlib.models.TreeViewModel;
     46 import com.android.hierarchyviewerlib.models.PixelPerfectModel.IImageChangeListener;
     47 import com.android.hierarchyviewerlib.models.TreeViewModel.ITreeChangeListener;
     48 import com.android.hierarchyviewerlib.ui.DeviceSelector;
     49 import com.android.hierarchyviewerlib.ui.LayoutViewer;
     50 import com.android.hierarchyviewerlib.ui.PixelPerfect;
     51 import com.android.hierarchyviewerlib.ui.PixelPerfectControls;
     52 import com.android.hierarchyviewerlib.ui.PixelPerfectLoupe;
     53 import com.android.hierarchyviewerlib.ui.PixelPerfectPixelPanel;
     54 import com.android.hierarchyviewerlib.ui.PixelPerfectTree;
     55 import com.android.hierarchyviewerlib.ui.PropertyViewer;
     56 import com.android.hierarchyviewerlib.ui.TreeView;
     57 import com.android.hierarchyviewerlib.ui.TreeViewControls;
     58 import com.android.hierarchyviewerlib.ui.TreeViewOverview;
     59 import com.android.menubar.IMenuBarEnhancer;
     60 import com.android.menubar.MenuBarEnhancer;
     61 import com.android.menubar.IMenuBarEnhancer.MenuBarMode;
     62 
     63 import org.eclipse.jface.action.MenuManager;
     64 import org.eclipse.jface.action.Separator;
     65 import org.eclipse.jface.window.ApplicationWindow;
     66 import org.eclipse.swt.SWT;
     67 import org.eclipse.swt.SWTException;
     68 import org.eclipse.swt.custom.SashForm;
     69 import org.eclipse.swt.custom.StackLayout;
     70 import org.eclipse.swt.events.SelectionEvent;
     71 import org.eclipse.swt.events.SelectionListener;
     72 import org.eclipse.swt.graphics.Image;
     73 import org.eclipse.swt.graphics.Rectangle;
     74 import org.eclipse.swt.layout.FillLayout;
     75 import org.eclipse.swt.layout.FormAttachment;
     76 import org.eclipse.swt.layout.FormData;
     77 import org.eclipse.swt.layout.FormLayout;
     78 import org.eclipse.swt.layout.GridData;
     79 import org.eclipse.swt.layout.GridLayout;
     80 import org.eclipse.swt.layout.RowLayout;
     81 import org.eclipse.swt.widgets.Button;
     82 import org.eclipse.swt.widgets.Composite;
     83 import org.eclipse.swt.widgets.Control;
     84 import org.eclipse.swt.widgets.Display;
     85 import org.eclipse.swt.widgets.Label;
     86 import org.eclipse.swt.widgets.ProgressBar;
     87 import org.eclipse.swt.widgets.Shell;
     88 
     89 public class HierarchyViewerApplication extends ApplicationWindow {
     90 
     91     private static final String APP_NAME = "Hierarchy Viewer";
     92     private static final int INITIAL_WIDTH = 1280;
     93     private static final int INITIAL_HEIGHT = 800;
     94 
     95     private static HierarchyViewerApplication sMainWindow;
     96 
     97     // Images for moving between the 3 main windows.
     98     private Image mDeviceViewImage;
     99     private Image mPixelPerfectImage;
    100     private Image mTreeViewImage;
    101     private Image mDeviceViewSelectedImage;
    102     private Image mPixelPerfectSelectedImage;
    103     private Image mTreeViewSelectedImage;
    104 
    105     // And their buttons
    106     private Button mTreeViewButton;
    107     private Button mPixelPerfectButton;
    108     private Button mDeviceViewButton;
    109 
    110     private Label mProgressLabel;
    111     private ProgressBar mProgressBar;
    112     private String mProgressString;
    113 
    114     private Composite mDeviceSelectorPanel;
    115     private Composite mTreeViewPanel;
    116     private Composite mPixelPerfectPanel;
    117     private StackLayout mMainWindowStackLayout;
    118     private DeviceSelector mDeviceSelector;
    119     private Composite mStatusBar;
    120     private TreeView mTreeView;
    121     private Composite mMainWindow;
    122     private Image mOnBlackImage;
    123     private Image mOnWhiteImage;
    124     private Button mOnBlackWhiteButton;
    125     private Button mShowExtras;
    126     private LayoutViewer mLayoutViewer;
    127     private PixelPerfectLoupe mPixelPerfectLoupe;
    128     private Composite mTreeViewControls;
    129 
    130     private ActionButton dumpDisplayList;
    131 
    132     private HierarchyViewerDirector mDirector;
    133 
    134     /*
    135      * If a thread bails with an uncaught exception, bring the whole
    136      * thing down.
    137      */
    138     private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
    139         public void uncaughtException(Thread t, Throwable e) {
    140             Log.e("HierarchyViewer", "shutting down due to uncaught exception");
    141             Log.e("HierarchyViewer", e);
    142             System.exit(1);
    143         }
    144     }
    145 
    146     public static final HierarchyViewerApplication getMainWindow() {
    147         return sMainWindow;
    148     }
    149 
    150     public HierarchyViewerApplication() {
    151         super(null /*shell*/);
    152 
    153         sMainWindow = this;
    154 
    155         addMenuBar();
    156     }
    157 
    158     @Override
    159     protected void configureShell(Shell shell) {
    160         super.configureShell(shell);
    161         shell.setText(APP_NAME);
    162         ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class);
    163         Image image = imageLoader.loadImage("sdk-hierarchyviewer-128.png", Display.getDefault()); //$NON-NLS-1$
    164         shell.setImage(image);
    165     }
    166 
    167     @Override
    168     public MenuManager createMenuManager() {
    169         return new MenuManager();
    170     }
    171 
    172     public void run() {
    173         setBlockOnOpen(true);
    174 
    175         try {
    176             open();
    177         } catch (SWTException e) {
    178          // Ignore "widget disposed" errors after we closed.
    179             if (!getShell().isDisposed()) {
    180                 throw e;
    181             }
    182         }
    183 
    184         TreeViewModel.getModel().removeTreeChangeListener(mTreeChangeListener);
    185         PixelPerfectModel.getModel().removeImageChangeListener(mImageChangeListener);
    186 
    187         ImageLoader.dispose();
    188         mDirector.stopListenForDevices();
    189         mDirector.stopDebugBridge();
    190         mDirector.terminate();
    191     }
    192 
    193     @Override
    194     protected void initializeBounds() {
    195         Rectangle monitorArea = Display.getDefault().getPrimaryMonitor().getBounds();
    196         getShell().setSize(Math.min(monitorArea.width, INITIAL_WIDTH),
    197                 Math.min(monitorArea.height, INITIAL_HEIGHT));
    198         getShell().setLocation(monitorArea.x + (monitorArea.width - INITIAL_WIDTH) / 2,
    199                 monitorArea.y + (monitorArea.height - INITIAL_HEIGHT) / 2);
    200     }
    201 
    202     private void loadResources() {
    203         ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class);
    204         mTreeViewImage = imageLoader.loadImage("tree-view.png", Display.getDefault()); //$NON-NLS-1$
    205         mTreeViewSelectedImage =
    206                 imageLoader.loadImage("tree-view-selected.png", Display.getDefault()); //$NON-NLS-1$
    207         mPixelPerfectImage = imageLoader.loadImage("pixel-perfect-view.png", Display.getDefault()); //$NON-NLS-1$
    208         mPixelPerfectSelectedImage =
    209                 imageLoader.loadImage("pixel-perfect-view-selected.png", Display.getDefault()); //$NON-NLS-1$
    210         mDeviceViewImage = imageLoader.loadImage("device-view.png", Display.getDefault()); //$NON-NLS-1$
    211         mDeviceViewSelectedImage =
    212                 imageLoader.loadImage("device-view-selected.png", Display.getDefault()); //$NON-NLS-1$
    213         mOnBlackImage = imageLoader.loadImage("on-black.png", Display.getDefault()); //$NON-NLS-1$
    214         mOnWhiteImage = imageLoader.loadImage("on-white.png", Display.getDefault()); //$NON-NLS-1$
    215     }
    216 
    217     @Override
    218     protected Control createContents(Composite parent) {
    219         // create this only once the window is opened to please SWT on Mac
    220         mDirector = HierarchyViewerApplicationDirector.createDirector();
    221         mDirector.initDebugBridge();
    222         mDirector.startListenForDevices();
    223         mDirector.populateDeviceSelectionModel();
    224 
    225         TreeViewModel.getModel().addTreeChangeListener(mTreeChangeListener);
    226         PixelPerfectModel.getModel().addImageChangeListener(mImageChangeListener);
    227 
    228         loadResources();
    229 
    230         Composite control = new Composite(parent, SWT.NONE);
    231         GridLayout mainLayout = new GridLayout();
    232         mainLayout.marginHeight = mainLayout.marginWidth = 0;
    233         mainLayout.verticalSpacing = mainLayout.horizontalSpacing = 0;
    234         control.setLayout(mainLayout);
    235         mMainWindow = new Composite(control, SWT.NONE);
    236         mMainWindow.setLayoutData(new GridData(GridData.FILL_BOTH));
    237         mMainWindowStackLayout = new StackLayout();
    238         mMainWindow.setLayout(mMainWindowStackLayout);
    239 
    240         buildDeviceSelectorPanel(mMainWindow);
    241         buildTreeViewPanel(mMainWindow);
    242         buildPixelPerfectPanel(mMainWindow);
    243 
    244         buildStatusBar(control);
    245 
    246         showDeviceSelector();
    247 
    248         return control;
    249     }
    250 
    251 
    252     private void buildStatusBar(Composite parent) {
    253         mStatusBar = new Composite(parent, SWT.NONE);
    254         mStatusBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    255 
    256         FormLayout statusBarLayout = new FormLayout();
    257         statusBarLayout.marginHeight = statusBarLayout.marginWidth = 2;
    258 
    259         mStatusBar.setLayout(statusBarLayout);
    260 
    261         mDeviceViewButton = new Button(mStatusBar, SWT.TOGGLE);
    262         mDeviceViewButton.setImage(mDeviceViewImage);
    263         mDeviceViewButton.setToolTipText("Switch to the window selection view");
    264         mDeviceViewButton.addSelectionListener(deviceViewButtonSelectionListener);
    265         FormData deviceViewButtonFormData = new FormData();
    266         deviceViewButtonFormData.left = new FormAttachment();
    267         mDeviceViewButton.setLayoutData(deviceViewButtonFormData);
    268 
    269         mTreeViewButton = new Button(mStatusBar, SWT.TOGGLE);
    270         mTreeViewButton.setImage(mTreeViewImage);
    271         mTreeViewButton.setEnabled(false);
    272         mTreeViewButton.setToolTipText("Switch to the tree view");
    273         mTreeViewButton.addSelectionListener(treeViewButtonSelectionListener);
    274         FormData treeViewButtonFormData = new FormData();
    275         treeViewButtonFormData.left = new FormAttachment(mDeviceViewButton, 2);
    276         mTreeViewButton.setLayoutData(treeViewButtonFormData);
    277 
    278         mPixelPerfectButton = new Button(mStatusBar, SWT.TOGGLE);
    279         mPixelPerfectButton.setImage(mPixelPerfectImage);
    280         mPixelPerfectButton.setEnabled(false);
    281         mPixelPerfectButton.setToolTipText("Switch to the pixel perfect view");
    282         mPixelPerfectButton.addSelectionListener(pixelPerfectButtonSelectionListener);
    283         FormData pixelPerfectButtonFormData = new FormData();
    284         pixelPerfectButtonFormData.left = new FormAttachment(mTreeViewButton, 2);
    285         mPixelPerfectButton.setLayoutData(pixelPerfectButtonFormData);
    286 
    287         // Tree View control panel...
    288         mTreeViewControls = new TreeViewControls(mStatusBar);
    289         FormData treeViewControlsFormData = new FormData();
    290         treeViewControlsFormData.left = new FormAttachment(mPixelPerfectButton, 2);
    291         treeViewControlsFormData.top = new FormAttachment(mTreeViewButton, 0, SWT.CENTER);
    292         treeViewControlsFormData.width = 552;
    293         mTreeViewControls.setLayoutData(treeViewControlsFormData);
    294 
    295         // Progress stuff
    296         mProgressLabel = new Label(mStatusBar, SWT.RIGHT);
    297 
    298         mProgressBar = new ProgressBar(mStatusBar, SWT.HORIZONTAL | SWT.INDETERMINATE | SWT.SMOOTH);
    299         FormData progressBarFormData = new FormData();
    300         progressBarFormData.right = new FormAttachment(100, 0);
    301         progressBarFormData.top = new FormAttachment(mTreeViewButton, 0, SWT.CENTER);
    302         mProgressBar.setLayoutData(progressBarFormData);
    303 
    304         FormData progressLabelFormData = new FormData();
    305         progressLabelFormData.right = new FormAttachment(mProgressBar, -2);
    306         progressLabelFormData.top = new FormAttachment(mTreeViewButton, 0, SWT.CENTER);
    307         mProgressLabel.setLayoutData(progressLabelFormData);
    308 
    309         if (mProgressString == null) {
    310             mProgressLabel.setVisible(false);
    311             mProgressBar.setVisible(false);
    312         } else {
    313             mProgressLabel.setText(mProgressString);
    314         }
    315     }
    316 
    317     private void buildDeviceSelectorPanel(Composite parent) {
    318         mDeviceSelectorPanel = new Composite(parent, SWT.NONE);
    319         GridLayout gridLayout = new GridLayout();
    320         gridLayout.marginWidth = gridLayout.marginHeight = 0;
    321         gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
    322         mDeviceSelectorPanel.setLayout(gridLayout);
    323 
    324         Composite buttonPanel = new Composite(mDeviceSelectorPanel, SWT.NONE);
    325         buttonPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    326 
    327         GridLayout buttonLayout = new GridLayout();
    328         buttonLayout.marginWidth = buttonLayout.marginHeight = 0;
    329         buttonLayout.horizontalSpacing = buttonLayout.verticalSpacing = 0;
    330         buttonPanel.setLayout(buttonLayout);
    331 
    332         Composite innerButtonPanel = new Composite(buttonPanel, SWT.NONE);
    333         innerButtonPanel.setLayoutData(new GridData(GridData.FILL_VERTICAL));
    334         GridLayout innerButtonPanelLayout = new GridLayout(3, true);
    335         innerButtonPanelLayout.marginWidth = innerButtonPanelLayout.marginHeight = 2;
    336         innerButtonPanelLayout.horizontalSpacing = innerButtonPanelLayout.verticalSpacing = 2;
    337         innerButtonPanel.setLayout(innerButtonPanelLayout);
    338 
    339         ActionButton refreshWindows =
    340                 new ActionButton(innerButtonPanel, RefreshWindowsAction.getAction());
    341         refreshWindows.setLayoutData(new GridData(GridData.FILL_BOTH));
    342 
    343         ActionButton loadViewHierarchyButton =
    344                 new ActionButton(innerButtonPanel, LoadViewHierarchyAction.getAction());
    345         loadViewHierarchyButton.setLayoutData(new GridData(GridData.FILL_BOTH));
    346 
    347         ActionButton inspectScreenshotButton =
    348                 new ActionButton(innerButtonPanel, InspectScreenshotAction.getAction());
    349         inspectScreenshotButton.setLayoutData(new GridData(GridData.FILL_BOTH));
    350 
    351         Composite deviceSelectorContainer = new Composite(mDeviceSelectorPanel, SWT.BORDER);
    352         deviceSelectorContainer.setLayoutData(new GridData(GridData.FILL_BOTH));
    353         deviceSelectorContainer.setLayout(new FillLayout());
    354         mDeviceSelector = new DeviceSelector(deviceSelectorContainer, true, true);
    355     }
    356 
    357     public void buildTreeViewPanel(Composite parent) {
    358         mTreeViewPanel = new Composite(parent, SWT.NONE);
    359         GridLayout gridLayout = new GridLayout();
    360         gridLayout.marginWidth = gridLayout.marginHeight = 0;
    361         gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
    362         mTreeViewPanel.setLayout(gridLayout);
    363 
    364         Composite buttonPanel = new Composite(mTreeViewPanel, SWT.NONE);
    365         buttonPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    366 
    367         GridLayout buttonLayout = new GridLayout();
    368         buttonLayout.marginWidth = buttonLayout.marginHeight = 0;
    369         buttonLayout.horizontalSpacing = buttonLayout.verticalSpacing = 0;
    370         buttonPanel.setLayout(buttonLayout);
    371 
    372         Composite innerButtonPanel = new Composite(buttonPanel, SWT.NONE);
    373         innerButtonPanel.setLayoutData(new GridData(GridData.FILL_VERTICAL));
    374         GridLayout innerButtonPanelLayout = new GridLayout(7, true);
    375         innerButtonPanelLayout.marginWidth = innerButtonPanelLayout.marginHeight = 2;
    376         innerButtonPanelLayout.horizontalSpacing = innerButtonPanelLayout.verticalSpacing = 2;
    377         innerButtonPanel.setLayout(innerButtonPanelLayout);
    378 
    379         ActionButton saveTreeView =
    380                 new ActionButton(innerButtonPanel, SaveTreeViewAction.getAction(getShell()));
    381         saveTreeView.setLayoutData(new GridData(GridData.FILL_BOTH));
    382 
    383         ActionButton capturePSD =
    384                 new ActionButton(innerButtonPanel, CapturePSDAction.getAction(getShell()));
    385         capturePSD.setLayoutData(new GridData(GridData.FILL_BOTH));
    386 
    387         ActionButton refreshViewAction =
    388                 new ActionButton(innerButtonPanel, RefreshViewAction.getAction());
    389         refreshViewAction.setLayoutData(new GridData(GridData.FILL_BOTH));
    390 
    391         ActionButton displayView =
    392                 new ActionButton(innerButtonPanel, DisplayViewAction.getAction(getShell()));
    393         displayView.setLayoutData(new GridData(GridData.FILL_BOTH));
    394 
    395         ActionButton invalidate = new ActionButton(innerButtonPanel, InvalidateAction.getAction());
    396         invalidate.setLayoutData(new GridData(GridData.FILL_BOTH));
    397 
    398         ActionButton requestLayout =
    399                 new ActionButton(innerButtonPanel, RequestLayoutAction.getAction());
    400         requestLayout.setLayoutData(new GridData(GridData.FILL_BOTH));
    401 
    402         dumpDisplayList =
    403                 new ActionButton(innerButtonPanel, DumpDisplayListAction.getAction());
    404         dumpDisplayList.setLayoutData(new GridData(GridData.FILL_BOTH));
    405 
    406         SashForm mainSash = new SashForm(mTreeViewPanel, SWT.HORIZONTAL | SWT.SMOOTH);
    407         mainSash.setLayoutData(new GridData(GridData.FILL_BOTH));
    408         Composite treeViewContainer = new Composite(mainSash, SWT.BORDER);
    409         treeViewContainer.setLayout(new FillLayout());
    410         mTreeView = new TreeView(treeViewContainer);
    411 
    412         SashForm sideSash = new SashForm(mainSash, SWT.VERTICAL | SWT.SMOOTH);
    413 
    414         mainSash.SASH_WIDTH = 4;
    415         mainSash.setWeights(new int[] {
    416                 7, 3
    417         });
    418 
    419         Composite treeViewOverviewContainer = new Composite(sideSash, SWT.BORDER);
    420         treeViewOverviewContainer.setLayout(new FillLayout());
    421         new TreeViewOverview(treeViewOverviewContainer);
    422 
    423         Composite propertyViewerContainer = new Composite(sideSash, SWT.BORDER);
    424         propertyViewerContainer.setLayout(new FillLayout());
    425         new PropertyViewer(propertyViewerContainer);
    426 
    427         Composite layoutViewerContainer = new Composite(sideSash, SWT.NONE);
    428         GridLayout layoutViewerLayout = new GridLayout();
    429         layoutViewerLayout.marginWidth = layoutViewerLayout.marginHeight = 0;
    430         layoutViewerLayout.horizontalSpacing = layoutViewerLayout.verticalSpacing = 1;
    431         layoutViewerContainer.setLayout(layoutViewerLayout);
    432 
    433         Composite fullButtonBar = new Composite(layoutViewerContainer, SWT.NONE);
    434         fullButtonBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    435         GridLayout fullButtonBarLayout = new GridLayout(2, false);
    436         fullButtonBarLayout.marginWidth = fullButtonBarLayout.marginHeight = 0;
    437         fullButtonBarLayout.marginRight = 2;
    438         fullButtonBarLayout.horizontalSpacing = fullButtonBarLayout.verticalSpacing = 0;
    439         fullButtonBar.setLayout(fullButtonBarLayout);
    440 
    441         Composite buttonBar = new Composite(fullButtonBar, SWT.NONE);
    442         buttonBar.setLayoutData(new GridData(GridData.FILL_VERTICAL));
    443         RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
    444         rowLayout.marginLeft =
    445                 rowLayout.marginRight = rowLayout.marginTop = rowLayout.marginBottom = 0;
    446         rowLayout.pack = true;
    447         rowLayout.center = true;
    448         buttonBar.setLayout(rowLayout);
    449 
    450         mOnBlackWhiteButton = new Button(buttonBar, SWT.PUSH);
    451         mOnBlackWhiteButton.setImage(mOnWhiteImage);
    452         mOnBlackWhiteButton.addSelectionListener(onBlackWhiteSelectionListener);
    453         mOnBlackWhiteButton.setToolTipText("Change layout viewer background color");
    454 
    455         mShowExtras = new Button(buttonBar, SWT.CHECK);
    456         mShowExtras.setText("Show Extras");
    457         mShowExtras.addSelectionListener(showExtrasSelectionListener);
    458         mShowExtras.setToolTipText("Show images");
    459 
    460         ActionButton loadAllViewsButton =
    461                 new ActionButton(fullButtonBar, LoadAllViewsAction.getAction());
    462         loadAllViewsButton.setLayoutData(new GridData(GridData.END, GridData.CENTER, true, true));
    463         loadAllViewsButton.addSelectionListener(loadAllViewsSelectionListener);
    464 
    465         Composite layoutViewerMainContainer = new Composite(layoutViewerContainer, SWT.BORDER);
    466         layoutViewerMainContainer.setLayoutData(new GridData(GridData.FILL_BOTH));
    467         layoutViewerMainContainer.setLayout(new FillLayout());
    468         mLayoutViewer = new LayoutViewer(layoutViewerMainContainer);
    469 
    470         sideSash.SASH_WIDTH = 4;
    471         sideSash.setWeights(new int[] {
    472                 238, 332, 416
    473         });
    474 
    475     }
    476 
    477     private void buildPixelPerfectPanel(Composite parent) {
    478         mPixelPerfectPanel = new Composite(parent, SWT.NONE);
    479         GridLayout gridLayout = new GridLayout();
    480         gridLayout.marginWidth = gridLayout.marginHeight = 0;
    481         gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
    482         mPixelPerfectPanel.setLayout(gridLayout);
    483 
    484         Composite buttonPanel = new Composite(mPixelPerfectPanel, SWT.NONE);
    485         buttonPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    486 
    487         GridLayout buttonLayout = new GridLayout();
    488         buttonLayout.marginWidth = buttonLayout.marginHeight = 0;
    489         buttonLayout.horizontalSpacing = buttonLayout.verticalSpacing = 0;
    490         buttonPanel.setLayout(buttonLayout);
    491 
    492         Composite innerButtonPanel = new Composite(buttonPanel, SWT.NONE);
    493         innerButtonPanel.setLayoutData(new GridData(GridData.FILL_VERTICAL));
    494         GridLayout innerButtonPanelLayout = new GridLayout(6, true);
    495         innerButtonPanelLayout.marginWidth = innerButtonPanelLayout.marginHeight = 2;
    496         innerButtonPanelLayout.horizontalSpacing = innerButtonPanelLayout.verticalSpacing = 2;
    497         innerButtonPanel.setLayout(innerButtonPanelLayout);
    498 
    499         ActionButton saveTreeView =
    500                 new ActionButton(innerButtonPanel, SavePixelPerfectAction.getAction(getShell()));
    501         saveTreeView.setLayoutData(new GridData(GridData.FILL_BOTH));
    502 
    503         ActionButton refreshPixelPerfect =
    504                 new ActionButton(innerButtonPanel, RefreshPixelPerfectAction.getAction());
    505         refreshPixelPerfect.setLayoutData(new GridData(GridData.FILL_BOTH));
    506 
    507         ActionButton refreshPixelPerfectTree =
    508                 new ActionButton(innerButtonPanel, RefreshPixelPerfectTreeAction.getAction());
    509         refreshPixelPerfectTree.setLayoutData(new GridData(GridData.FILL_BOTH));
    510 
    511         ActionButton loadOverlay =
    512                 new ActionButton(innerButtonPanel, LoadOverlayAction.getAction(getShell()));
    513         loadOverlay.setLayoutData(new GridData(GridData.FILL_BOTH));
    514 
    515         ActionButton showInLoupe =
    516                 new ActionButton(innerButtonPanel, ShowOverlayAction.getAction());
    517         showInLoupe.setLayoutData(new GridData(GridData.FILL_BOTH));
    518 
    519         ActionButton autoRefresh =
    520                 new ActionButton(innerButtonPanel, PixelPerfectAutoRefreshAction.getAction());
    521         autoRefresh.setLayoutData(new GridData(GridData.FILL_BOTH));
    522 
    523         SashForm mainSash = new SashForm(mPixelPerfectPanel, SWT.HORIZONTAL | SWT.SMOOTH);
    524         mainSash.setLayoutData(new GridData(GridData.FILL_BOTH));
    525         mainSash.SASH_WIDTH = 4;
    526 
    527         Composite pixelPerfectTreeContainer = new Composite(mainSash, SWT.BORDER);
    528         pixelPerfectTreeContainer.setLayout(new FillLayout());
    529         new PixelPerfectTree(pixelPerfectTreeContainer);
    530 
    531         Composite pixelPerfectLoupeContainer = new Composite(mainSash, SWT.NONE);
    532         GridLayout loupeLayout = new GridLayout();
    533         loupeLayout.marginWidth = loupeLayout.marginHeight = 0;
    534         loupeLayout.horizontalSpacing = loupeLayout.verticalSpacing = 0;
    535         pixelPerfectLoupeContainer.setLayout(loupeLayout);
    536 
    537         Composite pixelPerfectLoupeBorder = new Composite(pixelPerfectLoupeContainer, SWT.BORDER);
    538         pixelPerfectLoupeBorder.setLayoutData(new GridData(GridData.FILL_BOTH));
    539         GridLayout pixelPerfectLoupeBorderGridLayout = new GridLayout();
    540         pixelPerfectLoupeBorderGridLayout.marginWidth =
    541                 pixelPerfectLoupeBorderGridLayout.marginHeight = 0;
    542         pixelPerfectLoupeBorderGridLayout.horizontalSpacing =
    543                 pixelPerfectLoupeBorderGridLayout.verticalSpacing = 0;
    544         pixelPerfectLoupeBorder.setLayout(pixelPerfectLoupeBorderGridLayout);
    545 
    546         mPixelPerfectLoupe = new PixelPerfectLoupe(pixelPerfectLoupeBorder);
    547         mPixelPerfectLoupe.setLayoutData(new GridData(GridData.FILL_BOTH));
    548 
    549         PixelPerfectPixelPanel pixelPerfectPixelPanel =
    550                 new PixelPerfectPixelPanel(pixelPerfectLoupeBorder);
    551         pixelPerfectPixelPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    552 
    553         PixelPerfectControls pixelPerfectControls =
    554                 new PixelPerfectControls(pixelPerfectLoupeContainer);
    555         pixelPerfectControls.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    556 
    557 
    558         Composite pixelPerfectContainer = new Composite(mainSash, SWT.BORDER);
    559         pixelPerfectContainer.setLayout(new FillLayout());
    560         new PixelPerfect(pixelPerfectContainer);
    561 
    562         mainSash.setWeights(new int[] {
    563                 272, 376, 346
    564         });
    565 
    566     }
    567 
    568     public void showOverlayInLoupe(boolean value) {
    569         mPixelPerfectLoupe.setShowOverlay(value);
    570     }
    571 
    572     // Shows the progress notification...
    573     public void startTask(final String taskName) {
    574         mProgressString = taskName;
    575         Display.getDefault().syncExec(new Runnable() {
    576             public void run() {
    577                 if (mProgressLabel != null && mProgressBar != null) {
    578                     mProgressLabel.setText(taskName);
    579                     mProgressLabel.setVisible(true);
    580                     mProgressBar.setVisible(true);
    581                     mStatusBar.layout();
    582                 }
    583             }
    584         });
    585     }
    586 
    587     // And hides it!
    588     public void endTask() {
    589         mProgressString = null;
    590         Display.getDefault().syncExec(new Runnable() {
    591             public void run() {
    592                 if (mProgressLabel != null && mProgressBar != null) {
    593                     mProgressLabel.setVisible(false);
    594                     mProgressBar.setVisible(false);
    595                 }
    596             }
    597         });
    598     }
    599 
    600     public void showDeviceSelector() {
    601         // Show the menus.
    602         MenuManager mm = getMenuBarManager();
    603         mm.removeAll();
    604 
    605         MenuManager file = new MenuManager("&File");
    606         IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager(
    607                 APP_NAME,
    608                 getShell().getDisplay(),
    609                 file,
    610                 AboutAction.getAction(getShell()),
    611                 null /*preferencesAction*/,
    612                 QuitAction.getAction());
    613         if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
    614             mm.add(file);
    615         }
    616 
    617         MenuManager device = new MenuManager("&Devices");
    618         mm.add(device);
    619 
    620         device.add(RefreshWindowsAction.getAction());
    621         device.add(LoadViewHierarchyAction.getAction());
    622         device.add(InspectScreenshotAction.getAction());
    623 
    624         mm.updateAll(true);
    625 
    626         mDeviceViewButton.setSelection(true);
    627         mDeviceViewButton.setImage(mDeviceViewSelectedImage);
    628 
    629         mTreeViewButton.setSelection(false);
    630         mTreeViewButton.setImage(mTreeViewImage);
    631 
    632         mPixelPerfectButton.setSelection(false);
    633         mPixelPerfectButton.setImage(mPixelPerfectImage);
    634 
    635         mMainWindowStackLayout.topControl = mDeviceSelectorPanel;
    636 
    637         mMainWindow.layout();
    638 
    639         mDeviceSelector.setFocus();
    640 
    641         mTreeViewControls.setVisible(false);
    642     }
    643 
    644     public void showTreeView() {
    645         // Show the menus.
    646         MenuManager mm = getMenuBarManager();
    647         mm.removeAll();
    648 
    649         MenuManager file = new MenuManager("&File");
    650         IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager(
    651                 APP_NAME,
    652                 getShell().getDisplay(),
    653                 file,
    654                 AboutAction.getAction(getShell()),
    655                 null /*preferencesAction*/,
    656                 QuitAction.getAction());
    657         if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
    658             mm.add(file);
    659         }
    660 
    661         MenuManager treeViewMenu = new MenuManager("&Tree View");
    662         mm.add(treeViewMenu);
    663 
    664         treeViewMenu.add(SaveTreeViewAction.getAction(getShell()));
    665         treeViewMenu.add(CapturePSDAction.getAction(getShell()));
    666         treeViewMenu.add(new Separator());
    667         treeViewMenu.add(RefreshViewAction.getAction());
    668         treeViewMenu.add(DisplayViewAction.getAction(getShell()));
    669         // Only make the DumpDisplayList action visible if the protocol supports it.
    670         ViewServerInfo info = DeviceSelectionModel.getModel().getSelectedDeviceInfo();
    671         if (info != null && info.protocolVersion >= 4) {
    672             treeViewMenu.add(DumpDisplayListAction.getAction());
    673             dumpDisplayList.setVisible(true);
    674         } else {
    675             dumpDisplayList.setVisible(false);
    676         }
    677         treeViewMenu.add(new Separator());
    678         treeViewMenu.add(InvalidateAction.getAction());
    679         treeViewMenu.add(RequestLayoutAction.getAction());
    680 
    681         mm.updateAll(true);
    682 
    683         mDeviceViewButton.setSelection(false);
    684         mDeviceViewButton.setImage(mDeviceViewImage);
    685 
    686         mTreeViewButton.setSelection(true);
    687         mTreeViewButton.setImage(mTreeViewSelectedImage);
    688 
    689         mPixelPerfectButton.setSelection(false);
    690         mPixelPerfectButton.setImage(mPixelPerfectImage);
    691 
    692         mMainWindowStackLayout.topControl = mTreeViewPanel;
    693 
    694         mMainWindow.layout();
    695 
    696         mTreeView.setFocus();
    697 
    698         mTreeViewControls.setVisible(true);
    699     }
    700 
    701     public void showPixelPerfect() {
    702         // Show the menus.
    703         MenuManager mm = getMenuBarManager();
    704         mm.removeAll();
    705 
    706         MenuManager file = new MenuManager("&File");
    707         IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager(
    708                 APP_NAME,
    709                 getShell().getDisplay(),
    710                 file,
    711                 AboutAction.getAction(getShell()),
    712                 null /*preferencesAction*/,
    713                 QuitAction.getAction());
    714         if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
    715             mm.add(file);
    716         }
    717 
    718         MenuManager pixelPerfect = new MenuManager("&Pixel Perfect");
    719         pixelPerfect.add(SavePixelPerfectAction.getAction(getShell()));
    720         pixelPerfect.add(RefreshPixelPerfectAction.getAction());
    721         pixelPerfect.add(RefreshPixelPerfectTreeAction.getAction());
    722         pixelPerfect.add(PixelPerfectAutoRefreshAction.getAction());
    723         pixelPerfect.add(new Separator());
    724         pixelPerfect.add(LoadOverlayAction.getAction(getShell()));
    725         pixelPerfect.add(ShowOverlayAction.getAction());
    726 
    727         mm.add(pixelPerfect);
    728 
    729         mm.updateAll(true);
    730 
    731         mDeviceViewButton.setSelection(false);
    732         mDeviceViewButton.setImage(mDeviceViewImage);
    733 
    734         mTreeViewButton.setSelection(false);
    735         mTreeViewButton.setImage(mTreeViewImage);
    736 
    737         mPixelPerfectButton.setSelection(true);
    738         mPixelPerfectButton.setImage(mPixelPerfectSelectedImage);
    739 
    740         mMainWindowStackLayout.topControl = mPixelPerfectPanel;
    741 
    742         mMainWindow.layout();
    743 
    744         mPixelPerfectLoupe.setFocus();
    745 
    746         mTreeViewControls.setVisible(false);
    747     }
    748 
    749     private SelectionListener deviceViewButtonSelectionListener = new SelectionListener() {
    750         public void widgetDefaultSelected(SelectionEvent e) {
    751             // pass
    752         }
    753 
    754         public void widgetSelected(SelectionEvent e) {
    755             mDeviceViewButton.setSelection(true);
    756             showDeviceSelector();
    757         }
    758     };
    759 
    760     private SelectionListener treeViewButtonSelectionListener = new SelectionListener() {
    761         public void widgetDefaultSelected(SelectionEvent e) {
    762             // pass
    763         }
    764 
    765         public void widgetSelected(SelectionEvent e) {
    766             mTreeViewButton.setSelection(true);
    767             showTreeView();
    768         }
    769     };
    770 
    771     private SelectionListener pixelPerfectButtonSelectionListener = new SelectionListener() {
    772         public void widgetDefaultSelected(SelectionEvent e) {
    773             // pass
    774         }
    775 
    776         public void widgetSelected(SelectionEvent e) {
    777             mPixelPerfectButton.setSelection(true);
    778             showPixelPerfect();
    779         }
    780     };
    781 
    782     private SelectionListener onBlackWhiteSelectionListener = new SelectionListener() {
    783         public void widgetDefaultSelected(SelectionEvent e) {
    784             // pass
    785         }
    786 
    787         public void widgetSelected(SelectionEvent e) {
    788             if (mLayoutViewer.getOnBlack()) {
    789                 mLayoutViewer.setOnBlack(false);
    790                 mOnBlackWhiteButton.setImage(mOnBlackImage);
    791             } else {
    792                 mLayoutViewer.setOnBlack(true);
    793                 mOnBlackWhiteButton.setImage(mOnWhiteImage);
    794             }
    795         }
    796     };
    797 
    798     private SelectionListener showExtrasSelectionListener = new SelectionListener() {
    799         public void widgetDefaultSelected(SelectionEvent e) {
    800             // pass
    801         }
    802 
    803         public void widgetSelected(SelectionEvent e) {
    804             mLayoutViewer.setShowExtras(mShowExtras.getSelection());
    805         }
    806     };
    807 
    808     private SelectionListener loadAllViewsSelectionListener = new SelectionListener() {
    809         public void widgetDefaultSelected(SelectionEvent e) {
    810             // pass
    811         }
    812 
    813         public void widgetSelected(SelectionEvent e) {
    814             mShowExtras.setSelection(true);
    815             showExtrasSelectionListener.widgetSelected(null);
    816         }
    817     };
    818 
    819     private ITreeChangeListener mTreeChangeListener = new ITreeChangeListener() {
    820         public void selectionChanged() {
    821             // pass
    822         }
    823 
    824         public void treeChanged() {
    825             Display.getDefault().syncExec(new Runnable() {
    826                 public void run() {
    827                     if (TreeViewModel.getModel().getTree() == null) {
    828                         showDeviceSelector();
    829                         mTreeViewButton.setEnabled(false);
    830                     } else {
    831                         showTreeView();
    832                         mTreeViewButton.setEnabled(true);
    833                     }
    834                 }
    835             });
    836         }
    837 
    838         public void viewportChanged() {
    839             // pass
    840         }
    841 
    842         public void zoomChanged() {
    843             // pass
    844         }
    845     };
    846 
    847     private IImageChangeListener mImageChangeListener = new IImageChangeListener() {
    848 
    849         public void crosshairMoved() {
    850             // pass
    851         }
    852 
    853         public void treeChanged() {
    854             // pass
    855         }
    856 
    857         public void imageChanged() {
    858             // pass
    859         }
    860 
    861         public void imageLoaded() {
    862             Display.getDefault().syncExec(new Runnable() {
    863                 public void run() {
    864                     if (PixelPerfectModel.getModel().getImage() == null) {
    865                         mPixelPerfectButton.setEnabled(false);
    866                         showDeviceSelector();
    867                     } else {
    868                         mPixelPerfectButton.setEnabled(true);
    869                         showPixelPerfect();
    870                     }
    871                 }
    872             });
    873         }
    874 
    875         public void overlayChanged() {
    876             // pass
    877         }
    878 
    879         public void overlayTransparencyChanged() {
    880             // pass
    881         }
    882 
    883         public void selectionChanged() {
    884             // pass
    885         }
    886 
    887         public void zoomChanged() {
    888             // pass
    889         }
    890 
    891     };
    892 
    893     public static void main(String[] args) {
    894         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
    895 
    896         Display.setAppName("HierarchyViewer");
    897         new HierarchyViewerApplication().run();
    898     }
    899 }
    900