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