• 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.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
uncaughtException(Thread t, Throwable e)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 
getMainWindow()147     public static final HierarchyViewerApplication getMainWindow() {
148         return sMainWindow;
149     }
150 
HierarchyViewerApplication()151     public HierarchyViewerApplication() {
152         super(null /*shell*/);
153 
154         sMainWindow = this;
155 
156         addMenuBar();
157     }
158 
159     @Override
configureShell(Shell shell)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
createMenuManager()169     public MenuManager createMenuManager() {
170         return new MenuManager();
171     }
172 
run()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
initializeBounds()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 
loadResources()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
createContents(Composite parent)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 
buildStatusBar(Composite parent)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 
buildDeviceSelectorPanel(Composite parent)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 
buildTreeViewPanel(Composite parent)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 
buildPixelPerfectPanel(Composite parent)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 
showOverlayInLoupe(boolean value)569     public void showOverlayInLoupe(boolean value) {
570         mPixelPerfectLoupe.setShowOverlay(value);
571     }
572 
573     // Shows the progress notification...
startTask(final String taskName)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!
endTask()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 
showDeviceSelector()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 
showTreeView()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 
showPixelPerfect()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 
main(String[] args)922     public static void main(String[] args) {
923         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
924 
925         Display.setAppName("HierarchyViewer");
926         new HierarchyViewerApplication().run();
927     }
928 }
929