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