• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
uncaughtException(Thread t, Throwable e)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 
getMainWindow()146     public static final HierarchyViewerApplication getMainWindow() {
147         return sMainWindow;
148     }
149 
HierarchyViewerApplication()150     public HierarchyViewerApplication() {
151         super(null /*shell*/);
152 
153         sMainWindow = this;
154 
155         addMenuBar();
156     }
157 
158     @Override
configureShell(Shell shell)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
createMenuManager()168     public MenuManager createMenuManager() {
169         return new MenuManager();
170     }
171 
run()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
initializeBounds()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 
loadResources()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
createContents(Composite parent)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 
buildStatusBar(Composite parent)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 
buildDeviceSelectorPanel(Composite parent)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 
buildTreeViewPanel(Composite parent)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 
buildPixelPerfectPanel(Composite parent)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 
showOverlayInLoupe(boolean value)568     public void showOverlayInLoupe(boolean value) {
569         mPixelPerfectLoupe.setShowOverlay(value);
570     }
571 
572     // Shows the progress notification...
startTask(final String taskName)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!
endTask()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 
showDeviceSelector()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 
showTreeView()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 
showPixelPerfect()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 
main(String[] args)893     public static void main(String[] args) {
894         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
895 
896         Display.setAppName("HierarchyViewer");
897         new HierarchyViewerApplication().run();
898     }
899 }
900