1 /* 2 * Copyright (C) 2007 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.ddms; 18 19 import com.android.ddmlib.AndroidDebugBridge; 20 import com.android.ddmlib.Client; 21 import com.android.ddmlib.ClientData; 22 import com.android.ddmlib.IDevice; 23 import com.android.ddmlib.Log; 24 import com.android.ddmlib.SyncService; 25 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; 26 import com.android.ddmlib.ClientData.IHprofDumpHandler; 27 import com.android.ddmlib.ClientData.MethodProfilingStatus; 28 import com.android.ddmlib.Log.ILogOutput; 29 import com.android.ddmlib.Log.LogLevel; 30 import com.android.ddmlib.SyncService.SyncResult; 31 import com.android.ddmuilib.AllocationPanel; 32 import com.android.ddmuilib.DevicePanel; 33 import com.android.ddmuilib.EmulatorControlPanel; 34 import com.android.ddmuilib.HeapPanel; 35 import com.android.ddmuilib.ITableFocusListener; 36 import com.android.ddmuilib.ImageLoader; 37 import com.android.ddmuilib.InfoPanel; 38 import com.android.ddmuilib.NativeHeapPanel; 39 import com.android.ddmuilib.ScreenShotDialog; 40 import com.android.ddmuilib.SysinfoPanel; 41 import com.android.ddmuilib.TablePanel; 42 import com.android.ddmuilib.ThreadPanel; 43 import com.android.ddmuilib.DevicePanel.IUiSelectionListener; 44 import com.android.ddmuilib.actions.ToolItemAction; 45 import com.android.ddmuilib.explorer.DeviceExplorer; 46 import com.android.ddmuilib.handler.BaseFileHandler; 47 import com.android.ddmuilib.handler.MethodProfilingHandler; 48 import com.android.ddmuilib.log.event.EventLogPanel; 49 import com.android.ddmuilib.logcat.LogColors; 50 import com.android.ddmuilib.logcat.LogFilter; 51 import com.android.ddmuilib.logcat.LogPanel; 52 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager; 53 54 import org.eclipse.jface.dialogs.MessageDialog; 55 import org.eclipse.jface.preference.IPreferenceStore; 56 import org.eclipse.jface.preference.PreferenceStore; 57 import org.eclipse.swt.SWT; 58 import org.eclipse.swt.SWTError; 59 import org.eclipse.swt.SWTException; 60 import org.eclipse.swt.dnd.Clipboard; 61 import org.eclipse.swt.events.ControlEvent; 62 import org.eclipse.swt.events.ControlListener; 63 import org.eclipse.swt.events.MenuAdapter; 64 import org.eclipse.swt.events.MenuEvent; 65 import org.eclipse.swt.events.SelectionAdapter; 66 import org.eclipse.swt.events.SelectionEvent; 67 import org.eclipse.swt.events.ShellEvent; 68 import org.eclipse.swt.events.ShellListener; 69 import org.eclipse.swt.graphics.Color; 70 import org.eclipse.swt.graphics.Font; 71 import org.eclipse.swt.graphics.FontData; 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.widgets.Composite; 81 import org.eclipse.swt.widgets.Display; 82 import org.eclipse.swt.widgets.Event; 83 import org.eclipse.swt.widgets.Label; 84 import org.eclipse.swt.widgets.Listener; 85 import org.eclipse.swt.widgets.Menu; 86 import org.eclipse.swt.widgets.MenuItem; 87 import org.eclipse.swt.widgets.MessageBox; 88 import org.eclipse.swt.widgets.Sash; 89 import org.eclipse.swt.widgets.Shell; 90 import org.eclipse.swt.widgets.TabFolder; 91 import org.eclipse.swt.widgets.TabItem; 92 import org.eclipse.swt.widgets.ToolBar; 93 import org.eclipse.swt.widgets.ToolItem; 94 95 import java.io.File; 96 import java.util.ArrayList; 97 98 /** 99 * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an 100 * SWT application. So this class mainly builds the ui, and manages communication between the panels 101 * when {@link IDevice} / {@link Client} selection changes. 102 */ 103 public class UIThread implements IUiSelectionListener, IClientChangeListener { 104 /* 105 * UI tab panel definitions. The constants here must match up with the array 106 * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing 107 * the client list. 108 */ 109 public static final int PANEL_CLIENT_LIST = -1; 110 111 public static final int PANEL_INFO = 0; 112 113 public static final int PANEL_THREAD = 1; 114 115 public static final int PANEL_HEAP = 2; 116 117 public static final int PANEL_NATIVE_HEAP = 3; 118 119 private static final int PANEL_ALLOCATIONS = 4; 120 121 private static final int PANEL_SYSINFO = 5; 122 123 private static final int PANEL_COUNT = 6; 124 125 /** Content is setup in the constructor */ 126 private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT]; 127 128 private static final String[] mPanelNames = new String[] { 129 "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo" 130 }; 131 132 private static final String[] mPanelTips = new String[] { 133 "Client information", "Thread status", "VM heap status", 134 "Native heap status", "Allocation Tracker", "Sysinfo graphs" 135 }; 136 137 private static final String PREFERENCE_LOGSASH = 138 "logSashLocation"; //$NON-NLS-1$ 139 private static final String PREFERENCE_SASH = 140 "sashLocation"; //$NON-NLS-1$ 141 142 private static final String PREFS_COL_TIME = 143 "logcat.time"; //$NON-NLS-1$ 144 private static final String PREFS_COL_LEVEL = 145 "logcat.level"; //$NON-NLS-1$ 146 private static final String PREFS_COL_PID = 147 "logcat.pid"; //$NON-NLS-1$ 148 private static final String PREFS_COL_TAG = 149 "logcat.tag"; //$NON-NLS-1$ 150 private static final String PREFS_COL_MESSAGE = 151 "logcat.message"; //$NON-NLS-1$ 152 153 private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$ 154 155 // singleton instance 156 private static UIThread mInstance = new UIThread(); 157 158 // our display 159 private Display mDisplay; 160 161 // the table we show in the left-hand pane 162 private DevicePanel mDevicePanel; 163 164 private IDevice mCurrentDevice = null; 165 private Client mCurrentClient = null; 166 167 // status line at the bottom of the app window 168 private Label mStatusLine; 169 170 // some toolbar items we need to update 171 private ToolItem mTBShowThreadUpdates; 172 private ToolItem mTBShowHeapUpdates; 173 private ToolItem mTBHalt; 174 private ToolItem mTBCauseGc; 175 private ToolItem mTBDumpHprof; 176 private ToolItem mTBProfiling; 177 178 private final class FilterStorage implements ILogFilterStorageManager { 179 getFilterFromStore()180 public LogFilter[] getFilterFromStore() { 181 String filterPrefs = PrefsDialog.getStore().getString( 182 PREFS_FILTERS); 183 184 // split in a string per filter 185 String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$ 186 187 ArrayList<LogFilter> list = 188 new ArrayList<LogFilter>(filters.length); 189 190 for (String f : filters) { 191 if (f.length() > 0) { 192 LogFilter logFilter = new LogFilter(); 193 if (logFilter.loadFromString(f)) { 194 list.add(logFilter); 195 } 196 } 197 } 198 199 return list.toArray(new LogFilter[list.size()]); 200 } 201 saveFilters(LogFilter[] filters)202 public void saveFilters(LogFilter[] filters) { 203 StringBuilder sb = new StringBuilder(); 204 for (LogFilter f : filters) { 205 String filterString = f.toString(); 206 sb.append(filterString); 207 sb.append('|'); 208 } 209 210 PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString()); 211 } 212 requiresDefaultFilter()213 public boolean requiresDefaultFilter() { 214 return true; 215 } 216 } 217 218 219 private LogPanel mLogPanel; 220 221 private ToolItemAction mCreateFilterAction; 222 private ToolItemAction mDeleteFilterAction; 223 private ToolItemAction mEditFilterAction; 224 private ToolItemAction mExportAction; 225 private ToolItemAction mClearAction; 226 227 private ToolItemAction[] mLogLevelActions; 228 private String[] mLogLevelIcons = { 229 "v.png", //$NON-NLS-1S 230 "d.png", //$NON-NLS-1S 231 "i.png", //$NON-NLS-1S 232 "w.png", //$NON-NLS-1S 233 "e.png", //$NON-NLS-1S 234 }; 235 236 protected Clipboard mClipboard; 237 238 private MenuItem mCopyMenuItem; 239 240 private MenuItem mSelectAllMenuItem; 241 242 private TableFocusListener mTableListener; 243 244 private DeviceExplorer mExplorer = null; 245 private Shell mExplorerShell = null; 246 247 private EmulatorControlPanel mEmulatorPanel; 248 249 private EventLogPanel mEventLogPanel; 250 251 private Image mTracingStartImage; 252 253 private Image mTracingStopImage; 254 255 private ImageLoader mDdmUiLibLoader; 256 257 private class TableFocusListener implements ITableFocusListener { 258 259 private IFocusedTableActivator mCurrentActivator; 260 focusGained(IFocusedTableActivator activator)261 public void focusGained(IFocusedTableActivator activator) { 262 mCurrentActivator = activator; 263 if (mCopyMenuItem.isDisposed() == false) { 264 mCopyMenuItem.setEnabled(true); 265 mSelectAllMenuItem.setEnabled(true); 266 } 267 } 268 focusLost(IFocusedTableActivator activator)269 public void focusLost(IFocusedTableActivator activator) { 270 // if we move from one table to another, it's unclear 271 // if the old table lose its focus before the new 272 // one gets the focus, so we need to check. 273 if (activator == mCurrentActivator) { 274 activator = null; 275 if (mCopyMenuItem.isDisposed() == false) { 276 mCopyMenuItem.setEnabled(false); 277 mSelectAllMenuItem.setEnabled(false); 278 } 279 } 280 } 281 copy(Clipboard clipboard)282 public void copy(Clipboard clipboard) { 283 if (mCurrentActivator != null) { 284 mCurrentActivator.copy(clipboard); 285 } 286 } 287 selectAll()288 public void selectAll() { 289 if (mCurrentActivator != null) { 290 mCurrentActivator.selectAll(); 291 } 292 } 293 } 294 295 /** 296 * Handler for HPROF dumps. 297 * This will always prompt the user to save the HPROF file. 298 */ 299 private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler { 300 HProfHandler(Shell parentShell)301 public HProfHandler(Shell parentShell) { 302 super(parentShell); 303 } 304 onEndFailure(final Client client, final String message)305 public void onEndFailure(final Client client, final String message) { 306 mDisplay.asyncExec(new Runnable() { 307 public void run() { 308 try { 309 displayErrorFromUiThread( 310 "Unable to create HPROF file for application '%1$s'\n\n%2$s" + 311 "Check logcat for more information.", 312 client.getClientData().getClientDescription(), 313 message != null ? message + "\n\n" : ""); 314 } finally { 315 // this will make sure the dump hprof button is re-enabled for the 316 // current selection. as the client is finished dumping an hprof file 317 enableButtons(); 318 } 319 } 320 }); 321 } 322 onSuccess(final String remoteFilePath, final Client client)323 public void onSuccess(final String remoteFilePath, final Client client) { 324 mDisplay.asyncExec(new Runnable() { 325 public void run() { 326 final IDevice device = client.getDevice(); 327 try { 328 // get the sync service to pull the HPROF file 329 final SyncService sync = client.getDevice().getSyncService(); 330 if (sync != null) { 331 SyncResult result = promptAndPull(sync, 332 client.getClientData().getClientDescription() + ".hprof", 333 remoteFilePath, "Save HPROF file"); 334 if (result != null && result.getCode() != SyncService.RESULT_OK) { 335 displayErrorFromUiThread( 336 "Unable to download HPROF file from device '%1$s'.\n\n%2$s", 337 device.getSerialNumber(), result.getMessage()); 338 } 339 } else { 340 displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.", 341 device.getSerialNumber()); 342 } 343 } catch (Exception e) { 344 displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.", 345 device.getSerialNumber()); 346 347 } finally { 348 // this will make sure the dump hprof button is re-enabled for the 349 // current selection. as the client is finished dumping an hprof file 350 enableButtons(); 351 } 352 } 353 }); 354 } 355 onSuccess(final byte[] data, final Client client)356 public void onSuccess(final byte[] data, final Client client) { 357 mDisplay.asyncExec(new Runnable() { 358 public void run() { 359 promptAndSave(client.getClientData().getClientDescription() + ".hprof", data, 360 "Save HPROF file"); 361 } 362 }); 363 } 364 365 @Override getDialogTitle()366 protected String getDialogTitle() { 367 return "HPROF Error"; 368 } 369 } 370 371 372 /** 373 * Generic constructor. 374 */ UIThread()375 private UIThread() { 376 mPanels[PANEL_INFO] = new InfoPanel(); 377 mPanels[PANEL_THREAD] = new ThreadPanel(); 378 mPanels[PANEL_HEAP] = new HeapPanel(); 379 if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) { 380 mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel(); 381 } else { 382 mPanels[PANEL_NATIVE_HEAP] = null; 383 } 384 mPanels[PANEL_ALLOCATIONS] = new AllocationPanel(); 385 mPanels[PANEL_SYSINFO] = new SysinfoPanel(); 386 } 387 388 /** 389 * Get singleton instance of the UI thread. 390 */ getInstance()391 public static UIThread getInstance() { 392 return mInstance; 393 } 394 395 /** 396 * Return the Display. Don't try this unless you're in the UI thread. 397 */ getDisplay()398 public Display getDisplay() { 399 return mDisplay; 400 } 401 asyncExec(Runnable r)402 public void asyncExec(Runnable r) { 403 if (mDisplay != null && mDisplay.isDisposed() == false) { 404 mDisplay.asyncExec(r); 405 } 406 } 407 408 /** returns the IPreferenceStore */ getStore()409 public IPreferenceStore getStore() { 410 return PrefsDialog.getStore(); 411 } 412 413 /** 414 * Create SWT objects and drive the user interface event loop. 415 * @param location location of the folder that contains ddms. 416 */ runUI(String ddmsParentLocation)417 public void runUI(String ddmsParentLocation) { 418 Display.setAppName("ddms"); 419 mDisplay = new Display(); 420 final Shell shell = new Shell(mDisplay); 421 422 // create the image loaders for DDMS and DDMUILIB 423 mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader(); 424 425 shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay, 426 "ddms-icon.png", //$NON-NLS-1$ 427 100, 50, null)); 428 429 Log.setLogOutput(new ILogOutput() { 430 public void printAndPromptLog(final LogLevel logLevel, final String tag, 431 final String message) { 432 Log.printLog(logLevel, tag, message); 433 // dialog box only run in UI thread.. 434 mDisplay.asyncExec(new Runnable() { 435 public void run() { 436 Shell shell = mDisplay.getActiveShell(); 437 if (logLevel == LogLevel.ERROR) { 438 MessageDialog.openError(shell, tag, message); 439 } else { 440 MessageDialog.openWarning(shell, tag, message); 441 } 442 } 443 }); 444 } 445 446 public void printLog(LogLevel logLevel, String tag, String message) { 447 Log.printLog(logLevel, tag, message); 448 } 449 }); 450 451 // set the handler for hprof dump 452 ClientData.setHprofDumpHandler(new HProfHandler(shell)); 453 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell)); 454 455 // [try to] ensure ADB is running 456 String adbLocation; 457 if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) { 458 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$ 459 } else { 460 adbLocation = "adb"; //$NON-NLS-1$ 461 } 462 463 AndroidDebugBridge.init(true /* debugger support */); 464 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */); 465 466 // we need to listen to client change to be notified of client status (profiling) change 467 AndroidDebugBridge.addClientChangeListener(this); 468 469 shell.setText("Dalvik Debug Monitor"); 470 setConfirmClose(shell); 471 createMenus(shell); 472 createWidgets(shell); 473 474 shell.pack(); 475 setSizeAndPosition(shell); 476 shell.open(); 477 478 Log.d("ddms", "UI is up"); 479 480 while (!shell.isDisposed()) { 481 if (!mDisplay.readAndDispatch()) 482 mDisplay.sleep(); 483 } 484 mLogPanel.stopLogCat(true); 485 486 mDevicePanel.dispose(); 487 for (TablePanel panel : mPanels) { 488 if (panel != null) { 489 panel.dispose(); 490 } 491 } 492 493 ImageLoader.dispose(); 494 495 mDisplay.dispose(); 496 Log.d("ddms", "UI is down"); 497 } 498 499 /** 500 * Set the size and position of the main window from the preference, and 501 * setup listeners for control events (resize/move of the window) 502 */ setSizeAndPosition(final Shell shell)503 private void setSizeAndPosition(final Shell shell) { 504 shell.setMinimumSize(400, 200); 505 506 // get the x/y and w/h from the prefs 507 PreferenceStore prefs = PrefsDialog.getStore(); 508 int x = prefs.getInt(PrefsDialog.SHELL_X); 509 int y = prefs.getInt(PrefsDialog.SHELL_Y); 510 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH); 511 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT); 512 513 // check that we're not out of the display area 514 Rectangle rect = mDisplay.getClientArea(); 515 // first check the width/height 516 if (w > rect.width) { 517 w = rect.width; 518 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width); 519 } 520 if (h > rect.height) { 521 h = rect.height; 522 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height); 523 } 524 // then check x. Make sure the left corner is in the screen 525 if (x < rect.x) { 526 x = rect.x; 527 prefs.setValue(PrefsDialog.SHELL_X, rect.x); 528 } else if (x >= rect.x + rect.width) { 529 x = rect.x + rect.width - w; 530 prefs.setValue(PrefsDialog.SHELL_X, rect.x); 531 } 532 // then check y. Make sure the left corner is in the screen 533 if (y < rect.y) { 534 y = rect.y; 535 prefs.setValue(PrefsDialog.SHELL_Y, rect.y); 536 } else if (y >= rect.y + rect.height) { 537 y = rect.y + rect.height - h; 538 prefs.setValue(PrefsDialog.SHELL_Y, rect.y); 539 } 540 541 // now we can set the location/size 542 shell.setBounds(x, y, w, h); 543 544 // add listener for resize/move 545 shell.addControlListener(new ControlListener() { 546 public void controlMoved(ControlEvent e) { 547 // get the new x/y 548 Rectangle rect = shell.getBounds(); 549 // store in pref file 550 PreferenceStore prefs = PrefsDialog.getStore(); 551 prefs.setValue(PrefsDialog.SHELL_X, rect.x); 552 prefs.setValue(PrefsDialog.SHELL_Y, rect.y); 553 } 554 555 public void controlResized(ControlEvent e) { 556 // get the new w/h 557 Rectangle rect = shell.getBounds(); 558 // store in pref file 559 PreferenceStore prefs = PrefsDialog.getStore(); 560 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width); 561 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height); 562 } 563 }); 564 } 565 566 /** 567 * Set the size and position of the file explorer window from the 568 * preference, and setup listeners for control events (resize/move of 569 * the window) 570 */ setExplorerSizeAndPosition(final Shell shell)571 private void setExplorerSizeAndPosition(final Shell shell) { 572 shell.setMinimumSize(400, 200); 573 574 // get the x/y and w/h from the prefs 575 PreferenceStore prefs = PrefsDialog.getStore(); 576 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X); 577 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y); 578 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH); 579 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT); 580 581 // check that we're not out of the display area 582 Rectangle rect = mDisplay.getClientArea(); 583 // first check the width/height 584 if (w > rect.width) { 585 w = rect.width; 586 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width); 587 } 588 if (h > rect.height) { 589 h = rect.height; 590 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height); 591 } 592 // then check x. Make sure the left corner is in the screen 593 if (x < rect.x) { 594 x = rect.x; 595 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x); 596 } else if (x >= rect.x + rect.width) { 597 x = rect.x + rect.width - w; 598 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x); 599 } 600 // then check y. Make sure the left corner is in the screen 601 if (y < rect.y) { 602 y = rect.y; 603 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y); 604 } else if (y >= rect.y + rect.height) { 605 y = rect.y + rect.height - h; 606 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y); 607 } 608 609 // now we can set the location/size 610 shell.setBounds(x, y, w, h); 611 612 // add listener for resize/move 613 shell.addControlListener(new ControlListener() { 614 public void controlMoved(ControlEvent e) { 615 // get the new x/y 616 Rectangle rect = shell.getBounds(); 617 // store in pref file 618 PreferenceStore prefs = PrefsDialog.getStore(); 619 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x); 620 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y); 621 } 622 623 public void controlResized(ControlEvent e) { 624 // get the new w/h 625 Rectangle rect = shell.getBounds(); 626 // store in pref file 627 PreferenceStore prefs = PrefsDialog.getStore(); 628 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width); 629 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height); 630 } 631 }); 632 } 633 634 /* 635 * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO: 636 * is there any point in having this? 637 */ setConfirmClose(final Shell shell)638 private void setConfirmClose(final Shell shell) { 639 if (true) 640 return; 641 642 shell.addListener(SWT.Close, new Listener() { 643 public void handleEvent(Event event) { 644 int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO; 645 MessageBox msgBox = new MessageBox(shell, style); 646 msgBox.setText("Confirm..."); 647 msgBox.setMessage("Close DDM?"); 648 event.doit = (msgBox.open() == SWT.YES); 649 } 650 }); 651 } 652 653 /* 654 * Create the menu bar and items. 655 */ createMenus(final Shell shell)656 private void createMenus(final Shell shell) { 657 // create menu bar 658 Menu menuBar = new Menu(shell, SWT.BAR); 659 660 // create top-level items 661 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE); 662 fileItem.setText("&File"); 663 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE); 664 editItem.setText("&Edit"); 665 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE); 666 actionItem.setText("&Actions"); 667 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE); 668 deviceItem.setText("&Device"); 669 MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE); 670 helpItem.setText("&Help"); 671 672 // create top-level menus 673 Menu fileMenu = new Menu(menuBar); 674 fileItem.setMenu(fileMenu); 675 Menu editMenu = new Menu(menuBar); 676 editItem.setMenu(editMenu); 677 Menu actionMenu = new Menu(menuBar); 678 actionItem.setMenu(actionMenu); 679 Menu deviceMenu = new Menu(menuBar); 680 deviceItem.setMenu(deviceMenu); 681 Menu helpMenu = new Menu(menuBar); 682 helpItem.setMenu(helpMenu); 683 684 MenuItem item; 685 686 // create File menu items 687 item = new MenuItem(fileMenu, SWT.NONE); 688 item.setText("&Preferences..."); 689 item.addSelectionListener(new SelectionAdapter() { 690 @Override 691 public void widgetSelected(SelectionEvent e) { 692 PrefsDialog.run(shell); 693 } 694 }); 695 696 item = new MenuItem(fileMenu, SWT.NONE); 697 item.setText("&Static Port Configuration..."); 698 item.addSelectionListener(new SelectionAdapter() { 699 @Override 700 public void widgetSelected(SelectionEvent e) { 701 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell); 702 dlg.open(); 703 } 704 }); 705 706 new MenuItem(fileMenu, SWT.SEPARATOR); 707 708 item = new MenuItem(fileMenu, SWT.NONE); 709 item.setText("E&xit\tCtrl-Q"); 710 item.setAccelerator('Q' | SWT.CONTROL); 711 item.addSelectionListener(new SelectionAdapter() { 712 @Override 713 public void widgetSelected(SelectionEvent e) { 714 shell.close(); 715 } 716 }); 717 718 // create edit menu items 719 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE); 720 mCopyMenuItem.setText("&Copy\tCtrl-C"); 721 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND); 722 mCopyMenuItem.addSelectionListener(new SelectionAdapter() { 723 @Override 724 public void widgetSelected(SelectionEvent e) { 725 mTableListener.copy(mClipboard); 726 } 727 }); 728 729 new MenuItem(editMenu, SWT.SEPARATOR); 730 731 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE); 732 mSelectAllMenuItem.setText("Select &All\tCtrl-A"); 733 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND); 734 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() { 735 @Override 736 public void widgetSelected(SelectionEvent e) { 737 mTableListener.selectAll(); 738 } 739 }); 740 741 // create Action menu items 742 // TODO: this should come with a confirmation dialog 743 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE); 744 actionHaltItem.setText("&Halt VM"); 745 actionHaltItem.addSelectionListener(new SelectionAdapter() { 746 @Override 747 public void widgetSelected(SelectionEvent e) { 748 mDevicePanel.killSelectedClient(); 749 } 750 }); 751 752 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE); 753 actionCauseGcItem.setText("Cause &GC"); 754 actionCauseGcItem.addSelectionListener(new SelectionAdapter() { 755 @Override 756 public void widgetSelected(SelectionEvent e) { 757 mDevicePanel.forceGcOnSelectedClient(); 758 } 759 }); 760 761 // configure Action items based on current state 762 actionMenu.addMenuListener(new MenuAdapter() { 763 @Override 764 public void menuShown(MenuEvent e) { 765 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null); 766 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null); 767 } 768 }); 769 770 // create Device menu items 771 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE); 772 screenShotItem.setText("&Screen capture...\tCtrl-S"); 773 screenShotItem.setAccelerator('S' | SWT.CONTROL); 774 screenShotItem.addSelectionListener(new SelectionAdapter() { 775 @Override 776 public void widgetSelected(SelectionEvent e) { 777 if (mCurrentDevice != null) { 778 ScreenShotDialog dlg = new ScreenShotDialog(shell); 779 dlg.open(mCurrentDevice); 780 } 781 } 782 }); 783 784 new MenuItem(deviceMenu, SWT.SEPARATOR); 785 786 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE); 787 explorerItem.setText("File Explorer..."); 788 explorerItem.addSelectionListener(new SelectionAdapter() { 789 @Override 790 public void widgetSelected(SelectionEvent e) { 791 createFileExplorer(); 792 } 793 }); 794 795 new MenuItem(deviceMenu, SWT.SEPARATOR); 796 797 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE); 798 processItem.setText("Show &process status..."); 799 processItem.addSelectionListener(new SelectionAdapter() { 800 @Override 801 public void widgetSelected(SelectionEvent e) { 802 DeviceCommandDialog dlg; 803 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell); 804 dlg.open(mCurrentDevice); 805 } 806 }); 807 808 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE); 809 deviceStateItem.setText("Dump &device state..."); 810 deviceStateItem.addSelectionListener(new SelectionAdapter() { 811 @Override 812 public void widgetSelected(SelectionEvent e) { 813 DeviceCommandDialog dlg; 814 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0", 815 "device-state.txt", shell); 816 dlg.open(mCurrentDevice); 817 } 818 }); 819 820 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE); 821 appStateItem.setText("Dump &app state..."); 822 appStateItem.setEnabled(false); 823 appStateItem.addSelectionListener(new SelectionAdapter() { 824 @Override 825 public void widgetSelected(SelectionEvent e) { 826 DeviceCommandDialog dlg; 827 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell); 828 dlg.open(mCurrentDevice); 829 } 830 }); 831 832 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE); 833 radioStateItem.setText("Dump &radio state..."); 834 radioStateItem.addSelectionListener(new SelectionAdapter() { 835 @Override 836 public void widgetSelected(SelectionEvent e) { 837 DeviceCommandDialog dlg; 838 dlg = new DeviceCommandDialog( 839 "cat /data/logs/radio.4 /data/logs/radio.3" 840 + " /data/logs/radio.2 /data/logs/radio.1" 841 + " /data/logs/radio", 842 "radio-state.txt", shell); 843 dlg.open(mCurrentDevice); 844 } 845 }); 846 847 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE); 848 logCatItem.setText("Run &logcat..."); 849 logCatItem.addSelectionListener(new SelectionAdapter() { 850 @Override 851 public void widgetSelected(SelectionEvent e) { 852 DeviceCommandDialog dlg; 853 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt", 854 shell); 855 dlg.open(mCurrentDevice); 856 } 857 }); 858 859 // configure Action items based on current state 860 deviceMenu.addMenuListener(new MenuAdapter() { 861 @Override 862 public void menuShown(MenuEvent e) { 863 boolean deviceEnabled = mCurrentDevice != null; 864 screenShotItem.setEnabled(deviceEnabled); 865 explorerItem.setEnabled(deviceEnabled); 866 processItem.setEnabled(deviceEnabled); 867 deviceStateItem.setEnabled(deviceEnabled); 868 appStateItem.setEnabled(deviceEnabled); 869 radioStateItem.setEnabled(deviceEnabled); 870 logCatItem.setEnabled(deviceEnabled); 871 } 872 }); 873 874 // create Help menu items 875 item = new MenuItem(helpMenu, SWT.NONE); 876 item.setText("&Contents..."); 877 item.addSelectionListener(new SelectionAdapter() { 878 @Override 879 public void widgetSelected(SelectionEvent e) { 880 int style = SWT.APPLICATION_MODAL | SWT.OK; 881 MessageBox msgBox = new MessageBox(shell, style); 882 msgBox.setText("Help!"); 883 msgBox.setMessage("Help wanted."); 884 msgBox.open(); 885 } 886 }); 887 888 new MenuItem(helpMenu, SWT.SEPARATOR); 889 890 item = new MenuItem(helpMenu, SWT.NONE); 891 item.setText("&About..."); 892 item.addSelectionListener(new SelectionAdapter() { 893 @Override 894 public void widgetSelected(SelectionEvent e) { 895 AboutDialog dlg = new AboutDialog(shell); 896 dlg.open(); 897 } 898 }); 899 900 // tell the shell to use this menu 901 shell.setMenuBar(menuBar); 902 } 903 904 /* 905 * Create the widgets in the main application window. The basic layout is a 906 * two-panel sash, with a scrolling list of VMs on the left and detailed 907 * output for a single VM on the right. 908 */ createWidgets(final Shell shell)909 private void createWidgets(final Shell shell) { 910 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); 911 912 /* 913 * Create three areas: tool bar, split panels, status line 914 */ 915 shell.setLayout(new GridLayout(1, false)); 916 917 // 1. panel area 918 final Composite panelArea = new Composite(shell, SWT.BORDER); 919 920 // make the panel area absorb all space 921 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH)); 922 923 // 2. status line. 924 mStatusLine = new Label(shell, SWT.NONE); 925 926 // make status line extend all the way across 927 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 928 929 mStatusLine.setText("Initializing..."); 930 931 /* 932 * Configure the split-panel area. 933 */ 934 final PreferenceStore prefs = PrefsDialog.getStore(); 935 936 Composite topPanel = new Composite(panelArea, SWT.NONE); 937 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL); 938 sash.setBackground(darkGray); 939 Composite bottomPanel = new Composite(panelArea, SWT.NONE); 940 941 panelArea.setLayout(new FormLayout()); 942 943 createTopPanel(topPanel, darkGray); 944 createBottomPanel(bottomPanel); 945 946 // form layout data 947 FormData data = new FormData(); 948 data.top = new FormAttachment(0, 0); 949 data.bottom = new FormAttachment(sash, 0); 950 data.left = new FormAttachment(0, 0); 951 data.right = new FormAttachment(100, 0); 952 topPanel.setLayoutData(data); 953 954 final FormData sashData = new FormData(); 955 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) { 956 sashData.top = new FormAttachment(0, prefs.getInt( 957 PREFERENCE_LOGSASH)); 958 } else { 959 sashData.top = new FormAttachment(50,0); // 50% across 960 } 961 sashData.left = new FormAttachment(0, 0); 962 sashData.right = new FormAttachment(100, 0); 963 sash.setLayoutData(sashData); 964 965 data = new FormData(); 966 data.top = new FormAttachment(sash, 0); 967 data.bottom = new FormAttachment(100, 0); 968 data.left = new FormAttachment(0, 0); 969 data.right = new FormAttachment(100, 0); 970 bottomPanel.setLayoutData(data); 971 972 // allow resizes, but cap at minPanelWidth 973 sash.addListener(SWT.Selection, new Listener() { 974 public void handleEvent(Event e) { 975 Rectangle sashRect = sash.getBounds(); 976 Rectangle panelRect = panelArea.getClientArea(); 977 int bottom = panelRect.height - sashRect.height - 100; 978 e.y = Math.max(Math.min(e.y, bottom), 100); 979 if (e.y != sashRect.y) { 980 sashData.top = new FormAttachment(0, e.y); 981 prefs.setValue(PREFERENCE_LOGSASH, e.y); 982 panelArea.layout(); 983 } 984 } 985 }); 986 987 // add a global focus listener for all the tables 988 mTableListener = new TableFocusListener(); 989 990 // now set up the listener in the various panels 991 mLogPanel.setTableFocusListener(mTableListener); 992 mEventLogPanel.setTableFocusListener(mTableListener); 993 for (TablePanel p : mPanels) { 994 if (p != null) { 995 p.setTableFocusListener(mTableListener); 996 } 997 } 998 999 mStatusLine.setText(""); 1000 } 1001 1002 /* 1003 * Populate the tool bar. 1004 */ createDevicePanelToolBar(ToolBar toolBar)1005 private void createDevicePanelToolBar(ToolBar toolBar) { 1006 Display display = toolBar.getDisplay(); 1007 1008 // add "show heap updates" button 1009 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK); 1010 mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display, 1011 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1012 mTBShowHeapUpdates.setToolTipText("Show heap updates"); 1013 mTBShowHeapUpdates.setEnabled(false); 1014 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() { 1015 @Override 1016 public void widgetSelected(SelectionEvent e) { 1017 if (mCurrentClient != null) { 1018 // boolean status = ((ToolItem)e.item).getSelection(); 1019 // invert previous state 1020 boolean enable = !mCurrentClient.isHeapUpdateEnabled(); 1021 mCurrentClient.setHeapUpdateEnabled(enable); 1022 } else { 1023 e.doit = false; // this has no effect? 1024 } 1025 } 1026 }); 1027 1028 // add "dump HPROF" button 1029 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH); 1030 mTBDumpHprof.setToolTipText("Dump HPROF file"); 1031 mTBDumpHprof.setEnabled(false); 1032 mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display, 1033 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1034 mTBDumpHprof.addSelectionListener(new SelectionAdapter() { 1035 @Override 1036 public void widgetSelected(SelectionEvent e) { 1037 mDevicePanel.dumpHprof(); 1038 1039 // this will make sure the dump hprof button is disabled for the current selection 1040 // as the client is already dumping an hprof file 1041 enableButtons(); 1042 } 1043 }); 1044 1045 // add "cause GC" button 1046 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH); 1047 mTBCauseGc.setToolTipText("Cause an immediate GC"); 1048 mTBCauseGc.setEnabled(false); 1049 mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display, 1050 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1051 mTBCauseGc.addSelectionListener(new SelectionAdapter() { 1052 @Override 1053 public void widgetSelected(SelectionEvent e) { 1054 mDevicePanel.forceGcOnSelectedClient(); 1055 } 1056 }); 1057 1058 new ToolItem(toolBar, SWT.SEPARATOR); 1059 1060 // add "show thread updates" button 1061 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK); 1062 mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display, 1063 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1064 mTBShowThreadUpdates.setToolTipText("Show thread updates"); 1065 mTBShowThreadUpdates.setEnabled(false); 1066 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() { 1067 @Override 1068 public void widgetSelected(SelectionEvent e) { 1069 if (mCurrentClient != null) { 1070 // boolean status = ((ToolItem)e.item).getSelection(); 1071 // invert previous state 1072 boolean enable = !mCurrentClient.isThreadUpdateEnabled(); 1073 1074 mCurrentClient.setThreadUpdateEnabled(enable); 1075 } else { 1076 e.doit = false; // this has no effect? 1077 } 1078 } 1079 }); 1080 1081 // add a start/stop method tracing 1082 mTracingStartImage = mDdmUiLibLoader.loadImage(display, 1083 DevicePanel.ICON_TRACING_START, 1084 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null); 1085 mTracingStopImage = mDdmUiLibLoader.loadImage(display, 1086 DevicePanel.ICON_TRACING_STOP, 1087 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null); 1088 mTBProfiling = new ToolItem(toolBar, SWT.PUSH); 1089 mTBProfiling.setToolTipText("Start Method Profiling"); 1090 mTBProfiling.setEnabled(false); 1091 mTBProfiling.setImage(mTracingStartImage); 1092 mTBProfiling.addSelectionListener(new SelectionAdapter() { 1093 @Override 1094 public void widgetSelected(SelectionEvent e) { 1095 mDevicePanel.toggleMethodProfiling(); 1096 } 1097 }); 1098 1099 new ToolItem(toolBar, SWT.SEPARATOR); 1100 1101 // add "kill VM" button; need to make this visually distinct from 1102 // the status update buttons 1103 mTBHalt = new ToolItem(toolBar, SWT.PUSH); 1104 mTBHalt.setToolTipText("Halt the target VM"); 1105 mTBHalt.setEnabled(false); 1106 mTBHalt.setImage(mDdmUiLibLoader.loadImage(display, 1107 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1108 mTBHalt.addSelectionListener(new SelectionAdapter() { 1109 @Override 1110 public void widgetSelected(SelectionEvent e) { 1111 mDevicePanel.killSelectedClient(); 1112 } 1113 }); 1114 1115 toolBar.pack(); 1116 } 1117 createTopPanel(final Composite comp, Color darkGray)1118 private void createTopPanel(final Composite comp, Color darkGray) { 1119 final PreferenceStore prefs = PrefsDialog.getStore(); 1120 1121 comp.setLayout(new FormLayout()); 1122 1123 Composite leftPanel = new Composite(comp, SWT.NONE); 1124 final Sash sash = new Sash(comp, SWT.VERTICAL); 1125 sash.setBackground(darkGray); 1126 Composite rightPanel = new Composite(comp, SWT.NONE); 1127 1128 createLeftPanel(leftPanel); 1129 createRightPanel(rightPanel); 1130 1131 FormData data = new FormData(); 1132 data.top = new FormAttachment(0, 0); 1133 data.bottom = new FormAttachment(100, 0); 1134 data.left = new FormAttachment(0, 0); 1135 data.right = new FormAttachment(sash, 0); 1136 leftPanel.setLayoutData(data); 1137 1138 final FormData sashData = new FormData(); 1139 sashData.top = new FormAttachment(0, 0); 1140 sashData.bottom = new FormAttachment(100, 0); 1141 if (prefs != null && prefs.contains(PREFERENCE_SASH)) { 1142 sashData.left = new FormAttachment(0, prefs.getInt( 1143 PREFERENCE_SASH)); 1144 } else { 1145 // position the sash 380 from the right instead of x% (done by using 1146 // FormAttachment(x, 0)) in order to keep the sash at the same 1147 // position 1148 // from the left when the window is resized. 1149 // 380px is just enough to display the left table with no horizontal 1150 // scrollbar with the default font. 1151 sashData.left = new FormAttachment(0, 380); 1152 } 1153 sash.setLayoutData(sashData); 1154 1155 data = new FormData(); 1156 data.top = new FormAttachment(0, 0); 1157 data.bottom = new FormAttachment(100, 0); 1158 data.left = new FormAttachment(sash, 0); 1159 data.right = new FormAttachment(100, 0); 1160 rightPanel.setLayoutData(data); 1161 1162 final int minPanelWidth = 60; 1163 1164 // allow resizes, but cap at minPanelWidth 1165 sash.addListener(SWT.Selection, new Listener() { 1166 public void handleEvent(Event e) { 1167 Rectangle sashRect = sash.getBounds(); 1168 Rectangle panelRect = comp.getClientArea(); 1169 int right = panelRect.width - sashRect.width - minPanelWidth; 1170 e.x = Math.max(Math.min(e.x, right), minPanelWidth); 1171 if (e.x != sashRect.x) { 1172 sashData.left = new FormAttachment(0, e.x); 1173 prefs.setValue(PREFERENCE_SASH, e.x); 1174 comp.layout(); 1175 } 1176 } 1177 }); 1178 } 1179 createBottomPanel(final Composite comp)1180 private void createBottomPanel(final Composite comp) { 1181 final PreferenceStore prefs = PrefsDialog.getStore(); 1182 1183 // create clipboard 1184 Display display = comp.getDisplay(); 1185 mClipboard = new Clipboard(display); 1186 1187 LogColors colors = new LogColors(); 1188 1189 colors.infoColor = new Color(display, 0, 127, 0); 1190 colors.debugColor = new Color(display, 0, 0, 127); 1191 colors.errorColor = new Color(display, 255, 0, 0); 1192 colors.warningColor = new Color(display, 255, 127, 0); 1193 colors.verboseColor = new Color(display, 0, 0, 0); 1194 1195 // set the preferences names 1196 LogPanel.PREFS_TIME = PREFS_COL_TIME; 1197 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL; 1198 LogPanel.PREFS_PID = PREFS_COL_PID; 1199 LogPanel.PREFS_TAG = PREFS_COL_TAG; 1200 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE; 1201 1202 comp.setLayout(new GridLayout(1, false)); 1203 1204 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL); 1205 1206 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH); 1207 mCreateFilterAction.item.setToolTipText("Create Filter"); 1208 mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay, 1209 "add.png", //$NON-NLS-1$ 1210 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1211 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() { 1212 @Override 1213 public void widgetSelected(SelectionEvent e) { 1214 mLogPanel.addFilter(); 1215 } 1216 }); 1217 1218 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH); 1219 mEditFilterAction.item.setToolTipText("Edit Filter"); 1220 mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay, 1221 "edit.png", //$NON-NLS-1$ 1222 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1223 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() { 1224 @Override 1225 public void widgetSelected(SelectionEvent e) { 1226 mLogPanel.editFilter(); 1227 } 1228 }); 1229 1230 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH); 1231 mDeleteFilterAction.item.setToolTipText("Delete Filter"); 1232 mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay, 1233 "delete.png", //$NON-NLS-1$ 1234 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1235 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() { 1236 @Override 1237 public void widgetSelected(SelectionEvent e) { 1238 mLogPanel.deleteFilter(); 1239 } 1240 }); 1241 1242 1243 new ToolItem(toolBar, SWT.SEPARATOR); 1244 1245 LogLevel[] levels = LogLevel.values(); 1246 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length]; 1247 for (int i = 0 ; i < mLogLevelActions.length; i++) { 1248 String name = levels[i].getStringValue(); 1249 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK); 1250 mLogLevelActions[i] = newAction; 1251 //newAction.item.setText(name); 1252 newAction.item.addSelectionListener(new SelectionAdapter() { 1253 @Override 1254 public void widgetSelected(SelectionEvent e) { 1255 // disable the other actions and record current index 1256 for (int i = 0 ; i < mLogLevelActions.length; i++) { 1257 ToolItemAction a = mLogLevelActions[i]; 1258 if (a == newAction) { 1259 a.setChecked(true); 1260 1261 // set the log level 1262 mLogPanel.setCurrentFilterLogLevel(i+2); 1263 } else { 1264 a.setChecked(false); 1265 } 1266 } 1267 } 1268 }); 1269 1270 newAction.item.setToolTipText(name); 1271 newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay, 1272 mLogLevelIcons[i], 1273 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1274 } 1275 1276 new ToolItem(toolBar, SWT.SEPARATOR); 1277 1278 mClearAction = new ToolItemAction(toolBar, SWT.PUSH); 1279 mClearAction.item.setToolTipText("Clear Log"); 1280 1281 mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay, 1282 "clear.png", //$NON-NLS-1$ 1283 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1284 mClearAction.item.addSelectionListener(new SelectionAdapter() { 1285 @Override 1286 public void widgetSelected(SelectionEvent e) { 1287 mLogPanel.clear(); 1288 } 1289 }); 1290 1291 new ToolItem(toolBar, SWT.SEPARATOR); 1292 1293 mExportAction = new ToolItemAction(toolBar, SWT.PUSH); 1294 mExportAction.item.setToolTipText("Export Selection As Text..."); 1295 mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay, 1296 "save.png", //$NON-NLS-1$ 1297 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1298 mExportAction.item.addSelectionListener(new SelectionAdapter() { 1299 @Override 1300 public void widgetSelected(SelectionEvent e) { 1301 mLogPanel.save(); 1302 } 1303 }); 1304 1305 1306 toolBar.pack(); 1307 1308 // now create the log view 1309 mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL); 1310 1311 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions); 1312 1313 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE); 1314 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) { 1315 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO); 1316 } 1317 1318 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT); 1319 if (fontStr != null) { 1320 try { 1321 FontData fdat = new FontData(fontStr); 1322 mLogPanel.setFont(new Font(display, fdat)); 1323 } catch (IllegalArgumentException e) { 1324 // Looks like fontStr isn't a valid font representation. 1325 // We do nothing in this case, the logcat view will use the default font. 1326 } catch (SWTError e2) { 1327 // Looks like the Font() constructor failed. 1328 // We do nothing in this case, the logcat view will use the default font. 1329 } 1330 } 1331 1332 mLogPanel.createPanel(comp); 1333 1334 // and start the logcat 1335 mLogPanel.startLogCat(mCurrentDevice); 1336 } 1337 1338 /* 1339 * Create the contents of the left panel: a table of VMs. 1340 */ createLeftPanel(final Composite comp)1341 private void createLeftPanel(final Composite comp) { 1342 comp.setLayout(new GridLayout(1, false)); 1343 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP); 1344 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 1345 createDevicePanelToolBar(toolBar); 1346 1347 Composite c = new Composite(comp, SWT.NONE); 1348 c.setLayoutData(new GridData(GridData.FILL_BOTH)); 1349 1350 mDevicePanel = new DevicePanel(true /* showPorts */); 1351 mDevicePanel.createPanel(c); 1352 1353 // add ourselves to the device panel selection listener 1354 mDevicePanel.addSelectionListener(this); 1355 } 1356 1357 /* 1358 * Create the contents of the right panel: tabs with VM information. 1359 */ createRightPanel(final Composite comp)1360 private void createRightPanel(final Composite comp) { 1361 TabItem item; 1362 TabFolder tabFolder; 1363 1364 comp.setLayout(new FillLayout()); 1365 1366 tabFolder = new TabFolder(comp, SWT.NONE); 1367 1368 for (int i = 0; i < mPanels.length; i++) { 1369 if (mPanels[i] != null) { 1370 item = new TabItem(tabFolder, SWT.NONE); 1371 item.setText(mPanelNames[i]); 1372 item.setToolTipText(mPanelTips[i]); 1373 item.setControl(mPanels[i].createPanel(tabFolder)); 1374 } 1375 } 1376 1377 // add the emulator control panel to the folders. 1378 item = new TabItem(tabFolder, SWT.NONE); 1379 item.setText("Emulator Control"); 1380 item.setToolTipText("Emulator Control Panel"); 1381 mEmulatorPanel = new EmulatorControlPanel(); 1382 item.setControl(mEmulatorPanel.createPanel(tabFolder)); 1383 1384 // add the event log panel to the folders. 1385 item = new TabItem(tabFolder, SWT.NONE); 1386 item.setText("Event Log"); 1387 item.setToolTipText("Event Log"); 1388 1389 // create the composite that will hold the toolbar and the event log panel. 1390 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE); 1391 item.setControl(eventLogTopComposite); 1392 eventLogTopComposite.setLayout(new GridLayout(1, false)); 1393 1394 // create the toolbar and the actions 1395 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL); 1396 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 1397 1398 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH); 1399 optionsAction.item.setToolTipText("Opens the options panel"); 1400 optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(), 1401 "edit.png", //$NON-NLS-1$ 1402 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1403 1404 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH); 1405 clearAction.item.setToolTipText("Clears the event log"); 1406 clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(), 1407 "clear.png", //$NON-NLS-1$ 1408 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1409 1410 new ToolItem(toolbar, SWT.SEPARATOR); 1411 1412 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH); 1413 saveAction.item.setToolTipText("Saves the event log"); 1414 saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(), 1415 "save.png", //$NON-NLS-1$ 1416 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1417 1418 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH); 1419 loadAction.item.setToolTipText("Loads an event log"); 1420 loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(), 1421 "load.png", //$NON-NLS-1$ 1422 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1423 1424 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH); 1425 importBugAction.item.setToolTipText("Imports a bug report"); 1426 importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(), 1427 "importBug.png", //$NON-NLS-1$ 1428 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); 1429 1430 // create the event log panel 1431 mEventLogPanel = new EventLogPanel(); 1432 1433 // set the external actions 1434 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction, 1435 importBugAction); 1436 1437 // create the panel 1438 mEventLogPanel.createPanel(eventLogTopComposite); 1439 } 1440 createFileExplorer()1441 private void createFileExplorer() { 1442 if (mExplorer == null) { 1443 mExplorerShell = new Shell(mDisplay); 1444 1445 // create the ui 1446 mExplorerShell.setLayout(new GridLayout(1, false)); 1447 1448 // toolbar + action 1449 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL); 1450 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 1451 1452 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH); 1453 pullAction.item.setToolTipText("Pull File from Device"); 1454 Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$ 1455 if (image != null) { 1456 pullAction.item.setImage(image); 1457 } else { 1458 // this is for debugging purpose when the icon is missing 1459 pullAction.item.setText("Pull"); //$NON-NLS-1$ 1460 } 1461 1462 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH); 1463 pushAction.item.setToolTipText("Push file onto Device"); 1464 image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$ 1465 if (image != null) { 1466 pushAction.item.setImage(image); 1467 } else { 1468 // this is for debugging purpose when the icon is missing 1469 pushAction.item.setText("Push"); //$NON-NLS-1$ 1470 } 1471 1472 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH); 1473 deleteAction.item.setToolTipText("Delete"); 1474 image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$ 1475 if (image != null) { 1476 deleteAction.item.setImage(image); 1477 } else { 1478 // this is for debugging purpose when the icon is missing 1479 deleteAction.item.setText("Delete"); //$NON-NLS-1$ 1480 } 1481 1482 // device explorer 1483 mExplorer = new DeviceExplorer(); 1484 mExplorer.setActions(pushAction, pullAction, deleteAction); 1485 1486 pullAction.item.addSelectionListener(new SelectionAdapter() { 1487 @Override 1488 public void widgetSelected(SelectionEvent e) { 1489 mExplorer.pullSelection(); 1490 } 1491 }); 1492 pullAction.setEnabled(false); 1493 1494 pushAction.item.addSelectionListener(new SelectionAdapter() { 1495 @Override 1496 public void widgetSelected(SelectionEvent e) { 1497 mExplorer.pushIntoSelection(); 1498 } 1499 }); 1500 pushAction.setEnabled(false); 1501 1502 deleteAction.item.addSelectionListener(new SelectionAdapter() { 1503 @Override 1504 public void widgetSelected(SelectionEvent e) { 1505 mExplorer.deleteSelection(); 1506 } 1507 }); 1508 deleteAction.setEnabled(false); 1509 1510 Composite parent = new Composite(mExplorerShell, SWT.NONE); 1511 parent.setLayoutData(new GridData(GridData.FILL_BOTH)); 1512 1513 mExplorer.createPanel(parent); 1514 mExplorer.switchDevice(mCurrentDevice); 1515 1516 mExplorerShell.addShellListener(new ShellListener() { 1517 public void shellActivated(ShellEvent e) { 1518 // pass 1519 } 1520 1521 public void shellClosed(ShellEvent e) { 1522 mExplorer = null; 1523 mExplorerShell = null; 1524 } 1525 1526 public void shellDeactivated(ShellEvent e) { 1527 // pass 1528 } 1529 1530 public void shellDeiconified(ShellEvent e) { 1531 // pass 1532 } 1533 1534 public void shellIconified(ShellEvent e) { 1535 // pass 1536 } 1537 }); 1538 1539 mExplorerShell.pack(); 1540 setExplorerSizeAndPosition(mExplorerShell); 1541 mExplorerShell.open(); 1542 } else { 1543 if (mExplorerShell != null) { 1544 mExplorerShell.forceActive(); 1545 } 1546 } 1547 } 1548 1549 /** 1550 * Set the status line. TODO: make this a stack, so we can safely have 1551 * multiple things trying to set it all at once. Also specify an expiration? 1552 */ setStatusLine(final String str)1553 public void setStatusLine(final String str) { 1554 try { 1555 mDisplay.asyncExec(new Runnable() { 1556 public void run() { 1557 doSetStatusLine(str); 1558 } 1559 }); 1560 } catch (SWTException swte) { 1561 if (!mDisplay.isDisposed()) 1562 throw swte; 1563 } 1564 } 1565 doSetStatusLine(String str)1566 private void doSetStatusLine(String str) { 1567 if (mStatusLine.isDisposed()) 1568 return; 1569 1570 if (!mStatusLine.getText().equals(str)) { 1571 mStatusLine.setText(str); 1572 1573 // try { Thread.sleep(100); } 1574 // catch (InterruptedException ie) {} 1575 } 1576 } 1577 displayError(final String msg)1578 public void displayError(final String msg) { 1579 try { 1580 mDisplay.syncExec(new Runnable() { 1581 public void run() { 1582 MessageDialog.openError(mDisplay.getActiveShell(), "Error", 1583 msg); 1584 } 1585 }); 1586 } catch (SWTException swte) { 1587 if (!mDisplay.isDisposed()) 1588 throw swte; 1589 } 1590 } 1591 enableButtons()1592 private void enableButtons() { 1593 if (mCurrentClient != null) { 1594 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled()); 1595 mTBShowThreadUpdates.setEnabled(true); 1596 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled()); 1597 mTBShowHeapUpdates.setEnabled(true); 1598 mTBHalt.setEnabled(true); 1599 mTBCauseGc.setEnabled(true); 1600 1601 ClientData data = mCurrentClient.getClientData(); 1602 1603 if (data.hasFeature(ClientData.FEATURE_HPROF)) { 1604 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false); 1605 mTBDumpHprof.setToolTipText("Dump HPROF file"); 1606 } else { 1607 mTBDumpHprof.setEnabled(false); 1608 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)"); 1609 } 1610 1611 if (data.hasFeature(ClientData.FEATURE_PROFILING)) { 1612 mTBProfiling.setEnabled(true); 1613 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) { 1614 mTBProfiling.setToolTipText("Stop Method Profiling"); 1615 mTBProfiling.setImage(mTracingStopImage); 1616 } else { 1617 mTBProfiling.setToolTipText("Start Method Profiling"); 1618 mTBProfiling.setImage(mTracingStartImage); 1619 } 1620 } else { 1621 mTBProfiling.setEnabled(false); 1622 mTBProfiling.setImage(mTracingStartImage); 1623 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)"); 1624 } 1625 } else { 1626 // list is empty, disable these 1627 mTBShowThreadUpdates.setSelection(false); 1628 mTBShowThreadUpdates.setEnabled(false); 1629 mTBShowHeapUpdates.setSelection(false); 1630 mTBShowHeapUpdates.setEnabled(false); 1631 mTBHalt.setEnabled(false); 1632 mTBCauseGc.setEnabled(false); 1633 1634 mTBDumpHprof.setEnabled(false); 1635 mTBDumpHprof.setToolTipText("Dump HPROF file"); 1636 1637 mTBProfiling.setEnabled(false); 1638 mTBProfiling.setImage(mTracingStartImage); 1639 mTBProfiling.setToolTipText("Start Method Profiling"); 1640 } 1641 } 1642 1643 /** 1644 * Sent when a new {@link IDevice} and {@link Client} are selected. 1645 * @param selectedDevice the selected device. If null, no devices are selected. 1646 * @param selectedClient The selected client. If null, no clients are selected. 1647 * 1648 * @see IUiSelectionListener 1649 */ selectionChanged(IDevice selectedDevice, Client selectedClient)1650 public void selectionChanged(IDevice selectedDevice, Client selectedClient) { 1651 if (mCurrentDevice != selectedDevice) { 1652 mCurrentDevice = selectedDevice; 1653 for (TablePanel panel : mPanels) { 1654 if (panel != null) { 1655 panel.deviceSelected(mCurrentDevice); 1656 } 1657 } 1658 1659 mEmulatorPanel.deviceSelected(mCurrentDevice); 1660 mLogPanel.deviceSelected(mCurrentDevice); 1661 if (mEventLogPanel != null) { 1662 mEventLogPanel.deviceSelected(mCurrentDevice); 1663 } 1664 1665 if (mExplorer != null) { 1666 mExplorer.switchDevice(mCurrentDevice); 1667 } 1668 } 1669 1670 if (mCurrentClient != selectedClient) { 1671 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient); 1672 mCurrentClient = selectedClient; 1673 for (TablePanel panel : mPanels) { 1674 if (panel != null) { 1675 panel.clientSelected(mCurrentClient); 1676 } 1677 } 1678 1679 enableButtons(); 1680 } 1681 } 1682 clientChanged(Client client, int changeMask)1683 public void clientChanged(Client client, int changeMask) { 1684 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) == 1685 Client.CHANGE_METHOD_PROFILING_STATUS) { 1686 if (mCurrentClient == client) { 1687 mDisplay.asyncExec(new Runnable() { 1688 public void run() { 1689 // force refresh of the button enabled state. 1690 enableButtons(); 1691 } 1692 }); 1693 } 1694 } 1695 } 1696 } 1697