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