2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.ddms;
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.SyncException;
25 import com.android.ddmlib.SyncService;
26 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
27 import com.android.ddmlib.ClientData.IHprofDumpHandler;
28 import com.android.ddmlib.ClientData.MethodProfilingStatus;
29 import com.android.ddmlib.Log.ILogOutput;
30 import com.android.ddmlib.Log.LogLevel;
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 import com.android.menubar.IMenuBarCallback;
54 import com.android.menubar.IMenuBarEnhancer;
55 import com.android.menubar.IMenuBarEnhancer.MenuBarMode;
56 import com.android.menubar.MenuBarEnhancer;
58 import org.eclipse.jface.dialogs.MessageDialog;
59 import org.eclipse.jface.preference.IPreferenceStore;
60 import org.eclipse.jface.preference.PreferenceStore;
61 import org.eclipse.swt.SWT;
62 import org.eclipse.swt.SWTError;
63 import org.eclipse.swt.SWTException;
64 import org.eclipse.swt.dnd.Clipboard;
65 import org.eclipse.swt.events.ControlEvent;
66 import org.eclipse.swt.events.ControlListener;
67 import org.eclipse.swt.events.MenuAdapter;
68 import org.eclipse.swt.events.MenuEvent;
69 import org.eclipse.swt.events.SelectionAdapter;
70 import org.eclipse.swt.events.SelectionEvent;
71 import org.eclipse.swt.events.ShellEvent;
72 import org.eclipse.swt.events.ShellListener;
73 import org.eclipse.swt.graphics.Color;
74 import org.eclipse.swt.graphics.Font;
75 import org.eclipse.swt.graphics.FontData;
76 import org.eclipse.swt.graphics.Image;
77 import org.eclipse.swt.graphics.Rectangle;
78 import org.eclipse.swt.layout.FillLayout;
79 import org.eclipse.swt.layout.FormAttachment;
80 import org.eclipse.swt.layout.FormData;
81 import org.eclipse.swt.layout.FormLayout;
82 import org.eclipse.swt.layout.GridData;
83 import org.eclipse.swt.layout.GridLayout;
84 import org.eclipse.swt.widgets.Composite;
85 import org.eclipse.swt.widgets.Display;
86 import org.eclipse.swt.widgets.Event;
87 import org.eclipse.swt.widgets.Label;
88 import org.eclipse.swt.widgets.Listener;
89 import org.eclipse.swt.widgets.Menu;
90 import org.eclipse.swt.widgets.MenuItem;
91 import org.eclipse.swt.widgets.Sash;
92 import org.eclipse.swt.widgets.Shell;
93 import org.eclipse.swt.widgets.TabFolder;
94 import org.eclipse.swt.widgets.TabItem;
95 import org.eclipse.swt.widgets.ToolBar;
96 import org.eclipse.swt.widgets.ToolItem;
99 import java.util.ArrayList;
102 * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
103 * SWT application. So this class mainly builds the ui, and manages communication between the panels
104 * when {@link IDevice} / {@link Client} selection changes.
106 public class UIThread implements IUiSelectionListener, IClientChangeListener {
107 private static final String APP_NAME = "DDMS";
110 * UI tab panel definitions. The constants here must match up with the array
111 * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
114 public static final int PANEL_CLIENT_LIST = -1;
116 public static final int PANEL_INFO = 0;
118 public static final int PANEL_THREAD = 1;
120 public static final int PANEL_HEAP = 2;
122 public static final int PANEL_NATIVE_HEAP = 3;
124 private static final int PANEL_ALLOCATIONS = 4;
126 private static final int PANEL_SYSINFO = 5;
128 private static final int PANEL_COUNT = 6;
130 /** Content is setup in the constructor */
131 private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
133 private static final String[] mPanelNames = new String[] {
134 "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
137 private static final String[] mPanelTips = new String[] {
138 "Client information", "Thread status", "VM heap status",
139 "Native heap status", "Allocation Tracker", "Sysinfo graphs"
142 private static final String PREFERENCE_LOGSASH =
143 "logSashLocation"; //$NON-NLS-1$
144 private static final String PREFERENCE_SASH =
145 "sashLocation"; //$NON-NLS-1$
147 private static final String PREFS_COL_TIME =
148 "logcat.time"; //$NON-NLS-1$
149 private static final String PREFS_COL_LEVEL =
150 "logcat.level"; //$NON-NLS-1$
151 private static final String PREFS_COL_PID =
152 "logcat.pid"; //$NON-NLS-1$
153 private static final String PREFS_COL_TAG =
154 "logcat.tag"; //$NON-NLS-1$
155 private static final String PREFS_COL_MESSAGE =
156 "logcat.message"; //$NON-NLS-1$
158 private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
160 // singleton instance
161 private static UIThread mInstance = new UIThread();
164 private Display mDisplay;
166 // the table we show in the left-hand pane
167 private DevicePanel mDevicePanel;
169 private IDevice mCurrentDevice = null;
170 private Client mCurrentClient = null;
172 // status line at the bottom of the app window
173 private Label mStatusLine;
175 // some toolbar items we need to update
176 private ToolItem mTBShowThreadUpdates;
177 private ToolItem mTBShowHeapUpdates;
178 private ToolItem mTBHalt;
179 private ToolItem mTBCauseGc;
180 private ToolItem mTBDumpHprof;
181 private ToolItem mTBProfiling;
183 private final class FilterStorage implements ILogFilterStorageManager {
185 public LogFilter[] getFilterFromStore() {
186 String filterPrefs = PrefsDialog.getStore().getString(
189 // split in a string per filter
190 String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
192 ArrayList<LogFilter> list =
193 new ArrayList<LogFilter>(filters.length);
195 for (String f : filters) {
196 if (f.length() > 0) {
197 LogFilter logFilter = new LogFilter();
198 if (logFilter.loadFromString(f)) {
204 return list.toArray(new LogFilter[list.size()]);
207 public void saveFilters(LogFilter[] filters) {
208 StringBuilder sb = new StringBuilder();
209 for (LogFilter f : filters) {
210 String filterString = f.toString();
211 sb.append(filterString);
215 PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
218 public boolean requiresDefaultFilter() {
224 private LogPanel mLogPanel;
226 private ToolItemAction mCreateFilterAction;
227 private ToolItemAction mDeleteFilterAction;
228 private ToolItemAction mEditFilterAction;
229 private ToolItemAction mExportAction;
230 private ToolItemAction mClearAction;
232 private ToolItemAction[] mLogLevelActions;
233 private String[] mLogLevelIcons = {
234 "v.png", //$NON-NLS-1S
235 "d.png", //$NON-NLS-1S
236 "i.png", //$NON-NLS-1S
237 "w.png", //$NON-NLS-1S
238 "e.png", //$NON-NLS-1S
241 protected Clipboard mClipboard;
243 private MenuItem mCopyMenuItem;
245 private MenuItem mSelectAllMenuItem;
247 private TableFocusListener mTableListener;
249 private DeviceExplorer mExplorer = null;
250 private Shell mExplorerShell = null;
252 private EmulatorControlPanel mEmulatorPanel;
254 private EventLogPanel mEventLogPanel;
256 private Image mTracingStartImage;
258 private Image mTracingStopImage;
260 private ImageLoader mDdmUiLibLoader;
262 private class TableFocusListener implements ITableFocusListener {
264 private IFocusedTableActivator mCurrentActivator;
266 public void focusGained(IFocusedTableActivator activator) {
267 mCurrentActivator = activator;
268 if (mCopyMenuItem.isDisposed() == false) {
269 mCopyMenuItem.setEnabled(true);
270 mSelectAllMenuItem.setEnabled(true);
274 public void focusLost(IFocusedTableActivator activator) {
275 // if we move from one table to another, it's unclear
276 // if the old table lose its focus before the new
277 // one gets the focus, so we need to check.
278 if (activator == mCurrentActivator) {
280 if (mCopyMenuItem.isDisposed() == false) {
281 mCopyMenuItem.setEnabled(false);
282 mSelectAllMenuItem.setEnabled(false);
287 public void copy(Clipboard clipboard) {
288 if (mCurrentActivator != null) {
289 mCurrentActivator.copy(clipboard);
293 public void selectAll() {
294 if (mCurrentActivator != null) {
295 mCurrentActivator.selectAll();
301 * Handler for HPROF dumps.
302 * This will always prompt the user to save the HPROF file.
304 private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
306 public HProfHandler(Shell parentShell) {
310 public void onEndFailure(final Client client, final String message) {
311 mDisplay.asyncExec(new Runnable() {
314 displayErrorFromUiThread(
315 "Unable to create HPROF file for application '%1$s'\n\n%2$s" +
316 "Check logcat for more information.",
317 client.getClientData().getClientDescription(),
318 message != null ? message + "\n\n" : "");
320 // this will make sure the dump hprof button is re-enabled for the
321 // current selection. as the client is finished dumping an hprof file
328 public void onSuccess(final String remoteFilePath, final Client client) {
329 mDisplay.asyncExec(new Runnable() {
331 final IDevice device = client.getDevice();
333 // get the sync service to pull the HPROF file
334 final SyncService sync = client.getDevice().getSyncService();
337 client.getClientData().getClientDescription() + ".hprof",
338 remoteFilePath, "Save HPROF file");
340 displayErrorFromUiThread(
341 "Unable to download HPROF file from device '%1$s'.",
342 device.getSerialNumber());
344 } catch (SyncException e) {
345 if (e.wasCanceled() == false) {
346 displayErrorFromUiThread(
347 "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
348 device.getSerialNumber(), e.getMessage());
350 } catch (Exception e) {
351 displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.",
352 device.getSerialNumber());
355 // this will make sure the dump hprof button is re-enabled for the
356 // current selection. as the client is finished dumping an hprof file
363 public void onSuccess(final byte[] data, final Client client) {
364 mDisplay.asyncExec(new Runnable() {
366 promptAndSave(client.getClientData().getClientDescription() + ".hprof", data,
373 protected String getDialogTitle() {
374 return "HPROF Error";
380 * Generic constructor.
383 mPanels[PANEL_INFO] = new InfoPanel();
384 mPanels[PANEL_THREAD] = new ThreadPanel();
385 mPanels[PANEL_HEAP] = new HeapPanel();
386 if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
387 mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
389 mPanels[PANEL_NATIVE_HEAP] = null;
391 mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
392 mPanels[PANEL_SYSINFO] = new SysinfoPanel();
396 * Get singleton instance of the UI thread.
398 public static UIThread getInstance() {
403 * Return the Display. Don't try this unless you're in the UI thread.
405 public Display getDisplay() {
409 public void asyncExec(Runnable r) {
410 if (mDisplay != null && mDisplay.isDisposed() == false) {
411 mDisplay.asyncExec(r);
415 /** returns the IPreferenceStore */
416 public IPreferenceStore getStore() {
417 return PrefsDialog.getStore();
421 * Create SWT objects and drive the user interface event loop.
422 * @param ddmsParentLocation location of the folder that contains ddms.
424 public void runUI(String ddmsParentLocation) {
425 Display.setAppName(APP_NAME);
426 mDisplay = new Display();
427 final Shell shell = new Shell(mDisplay);
429 // create the image loaders for DDMS and DDMUILIB
430 mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader();
432 shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay,
433 "ddms-128.png", //$NON-NLS-1$
436 Log.setLogOutput(new ILogOutput() {
437 public void printAndPromptLog(final LogLevel logLevel, final String tag,
438 final String message) {
439 Log.printLog(logLevel, tag, message);
440 // dialog box only run in UI thread..
441 mDisplay.asyncExec(new Runnable() {
443 Shell activeShell = mDisplay.getActiveShell();
444 if (logLevel == LogLevel.ERROR) {
445 MessageDialog.openError(activeShell, tag, message);
447 MessageDialog.openWarning(activeShell, tag, message);
453 public void printLog(LogLevel logLevel, String tag, String message) {
454 Log.printLog(logLevel, tag, message);
458 // set the handler for hprof dump
459 ClientData.setHprofDumpHandler(new HProfHandler(shell));
460 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
462 // [try to] ensure ADB is running
463 // in the new SDK, adb is in the platform-tools, but when run from the command line
464 // in the Android source tree, then adb is next to ddms.
466 if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
467 // check if there's a platform-tools folder
468 File platformTools = new File(new File(ddmsParentLocation).getParent(),
469 "platform-tools"); //$NON-NLS-1$
470 if (platformTools.isDirectory()) {
471 adbLocation = platformTools.getAbsolutePath() + File.separator + "adb"; //$NON-NLS-1$
473 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
476 adbLocation = "adb"; //$NON-NLS-1$
479 AndroidDebugBridge.init(true /* debugger support */);
480 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
482 // we need to listen to client change to be notified of client status (profiling) change
483 AndroidDebugBridge.addClientChangeListener(this);
485 shell.setText("Dalvik Debug Monitor");
486 setConfirmClose(shell);
488 createWidgets(shell);
491 setSizeAndPosition(shell);
494 Log.d("ddms", "UI is up");
496 while (!shell.isDisposed()) {
497 if (!mDisplay.readAndDispatch())
500 mLogPanel.stopLogCat(true);
502 mDevicePanel.dispose();
503 for (TablePanel panel : mPanels) {
509 ImageLoader.dispose();
512 Log.d("ddms", "UI is down");
516 * Set the size and position of the main window from the preference, and
517 * setup listeners for control events (resize/move of the window)
519 private void setSizeAndPosition(final Shell shell) {
520 shell.setMinimumSize(400, 200);
522 // get the x/y and w/h from the prefs
523 PreferenceStore prefs = PrefsDialog.getStore();
524 int x = prefs.getInt(PrefsDialog.SHELL_X);
525 int y = prefs.getInt(PrefsDialog.SHELL_Y);
526 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
527 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
529 // check that we're not out of the display area
530 Rectangle rect = mDisplay.getClientArea();
531 // first check the width/height
532 if (w > rect.width) {
534 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
536 if (h > rect.height) {
538 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
540 // then check x. Make sure the left corner is in the screen
543 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
544 } else if (x >= rect.x + rect.width) {
545 x = rect.x + rect.width - w;
546 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
548 // then check y. Make sure the left corner is in the screen
551 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
552 } else if (y >= rect.y + rect.height) {
553 y = rect.y + rect.height - h;
554 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
557 // now we can set the location/size
558 shell.setBounds(x, y, w, h);
560 // add listener for resize/move
561 shell.addControlListener(new ControlListener() {
562 public void controlMoved(ControlEvent e) {
564 Rectangle controlBounds = shell.getBounds();
565 // store in pref file
566 PreferenceStore currentPrefs = PrefsDialog.getStore();
567 currentPrefs.setValue(PrefsDialog.SHELL_X, controlBounds.x);
568 currentPrefs.setValue(PrefsDialog.SHELL_Y, controlBounds.y);
571 public void controlResized(ControlEvent e) {
573 Rectangle controlBounds = shell.getBounds();
574 // store in pref file
575 PreferenceStore currentPrefs = PrefsDialog.getStore();
576 currentPrefs.setValue(PrefsDialog.SHELL_WIDTH, controlBounds.width);
577 currentPrefs.setValue(PrefsDialog.SHELL_HEIGHT, controlBounds.height);
583 * Set the size and position of the file explorer window from the
584 * preference, and setup listeners for control events (resize/move of
587 private void setExplorerSizeAndPosition(final Shell shell) {
588 shell.setMinimumSize(400, 200);
590 // get the x/y and w/h from the prefs
591 PreferenceStore prefs = PrefsDialog.getStore();
592 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
593 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
594 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
595 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
597 // check that we're not out of the display area
598 Rectangle rect = mDisplay.getClientArea();
599 // first check the width/height
600 if (w > rect.width) {
602 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
604 if (h > rect.height) {
606 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
608 // then check x. Make sure the left corner is in the screen
611 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
612 } else if (x >= rect.x + rect.width) {
613 x = rect.x + rect.width - w;
614 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
616 // then check y. Make sure the left corner is in the screen
619 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
620 } else if (y >= rect.y + rect.height) {
621 y = rect.y + rect.height - h;
622 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
625 // now we can set the location/size
626 shell.setBounds(x, y, w, h);
628 // add listener for resize/move
629 shell.addControlListener(new ControlListener() {
630 public void controlMoved(ControlEvent e) {
632 Rectangle controlBounds = shell.getBounds();
633 // store in pref file
634 PreferenceStore currentPrefs = PrefsDialog.getStore();
635 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_X, controlBounds.x);
636 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, controlBounds.y);
639 public void controlResized(ControlEvent e) {
641 Rectangle controlBounds = shell.getBounds();
642 // store in pref file
643 PreferenceStore currentPrefs = PrefsDialog.getStore();
644 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, controlBounds.width);
645 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, controlBounds.height);
651 * Set the confirm-before-close dialog.
653 private void setConfirmClose(final Shell shell) {
654 // Note: there was some commented out code to display a confirmation box
655 // when closing. The feature seems unnecessary and the code was not being
656 // used, so it has been removed.
660 * Create the menu bar and items.
662 private void createMenus(final Shell shell) {
664 Menu menuBar = new Menu(shell, SWT.BAR);
666 // create top-level items
667 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
668 fileItem.setText("&File");
669 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
670 editItem.setText("&Edit");
671 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
672 actionItem.setText("&Actions");
673 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
674 deviceItem.setText("&Device");
676 // create top-level menus
677 Menu fileMenu = new Menu(menuBar);
678 fileItem.setMenu(fileMenu);
679 Menu editMenu = new Menu(menuBar);
680 editItem.setMenu(editMenu);
681 Menu actionMenu = new Menu(menuBar);
682 actionItem.setMenu(actionMenu);
683 Menu deviceMenu = new Menu(menuBar);
684 deviceItem.setMenu(deviceMenu);
688 // create File menu items
689 item = new MenuItem(fileMenu, SWT.NONE);
690 item.setText("&Static Port Configuration...");
691 item.addSelectionListener(new SelectionAdapter() {
693 public void widgetSelected(SelectionEvent e) {
694 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
699 IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenu(APP_NAME, fileMenu,
700 new IMenuBarCallback() {
701 public void printError(String format, Object... args) {
702 Log.e("DDMS Menu Bar", String.format(format, args));
705 public void onPreferencesMenuSelected() {
706 PrefsDialog.run(shell);
709 public void onAboutMenuSelected() {
710 AboutDialog dlg = new AboutDialog(shell);
715 if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
716 new MenuItem(fileMenu, SWT.SEPARATOR);
718 item = new MenuItem(fileMenu, SWT.NONE);
719 item.setText("E&xit\tCtrl-Q");
720 item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
721 item.addSelectionListener(new SelectionAdapter() {
723 public void widgetSelected(SelectionEvent e) {
729 // create edit menu items
730 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
731 mCopyMenuItem.setText("&Copy\tCtrl-C");
732 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
733 mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
735 public void widgetSelected(SelectionEvent e) {
736 mTableListener.copy(mClipboard);
740 new MenuItem(editMenu, SWT.SEPARATOR);
742 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
743 mSelectAllMenuItem.setText("Select &All\tCtrl-A");
744 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
745 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
747 public void widgetSelected(SelectionEvent e) {
748 mTableListener.selectAll();
752 // create Action menu items
753 // TODO: this should come with a confirmation dialog
754 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
755 actionHaltItem.setText("&Halt VM");
756 actionHaltItem.addSelectionListener(new SelectionAdapter() {
758 public void widgetSelected(SelectionEvent e) {
759 mDevicePanel.killSelectedClient();
763 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
764 actionCauseGcItem.setText("Cause &GC");
765 actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
767 public void widgetSelected(SelectionEvent e) {
768 mDevicePanel.forceGcOnSelectedClient();
772 final MenuItem actionResetAdb = new MenuItem(actionMenu, SWT.NONE);
773 actionResetAdb.setText("&Reset adb");
774 actionResetAdb.addSelectionListener(new SelectionAdapter() {
776 public void widgetSelected(SelectionEvent e) {
777 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
778 if (bridge != null) {
784 // configure Action items based on current state
785 actionMenu.addMenuListener(new MenuAdapter() {
787 public void menuShown(MenuEvent e) {
788 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
789 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
790 actionResetAdb.setEnabled(true);
794 // create Device menu items
795 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
797 // The \tCtrl-S "keybinding text" here isn't right for the Mac - but
798 // it's stripped out and replaced by the proper keyboard accelerator
799 // text (e.g. the unicode symbol for the command key + S) anyway
800 // so it's fine to leave it there for the other platforms.
801 screenShotItem.setText("&Screen capture...\tCtrl-S");
802 screenShotItem.setAccelerator('S' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
803 screenShotItem.addSelectionListener(new SelectionAdapter() {
805 public void widgetSelected(SelectionEvent e) {
806 if (mCurrentDevice != null) {
807 ScreenShotDialog dlg = new ScreenShotDialog(shell);
808 dlg.open(mCurrentDevice);
813 new MenuItem(deviceMenu, SWT.SEPARATOR);
815 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
816 explorerItem.setText("File Explorer...");
817 explorerItem.addSelectionListener(new SelectionAdapter() {
819 public void widgetSelected(SelectionEvent e) {
820 createFileExplorer();
824 new MenuItem(deviceMenu, SWT.SEPARATOR);
826 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
827 processItem.setText("Show &process status...");
828 processItem.addSelectionListener(new SelectionAdapter() {
830 public void widgetSelected(SelectionEvent e) {
831 DeviceCommandDialog dlg;
832 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
833 dlg.open(mCurrentDevice);
837 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
838 deviceStateItem.setText("Dump &device state...");
839 deviceStateItem.addSelectionListener(new SelectionAdapter() {
841 public void widgetSelected(SelectionEvent e) {
842 DeviceCommandDialog dlg;
843 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
844 "device-state.txt", shell);
845 dlg.open(mCurrentDevice);
849 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
850 appStateItem.setText("Dump &app state...");
851 appStateItem.setEnabled(false);
852 appStateItem.addSelectionListener(new SelectionAdapter() {
854 public void widgetSelected(SelectionEvent e) {
855 DeviceCommandDialog dlg;
856 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
857 dlg.open(mCurrentDevice);
861 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
862 radioStateItem.setText("Dump &radio state...");
863 radioStateItem.addSelectionListener(new SelectionAdapter() {
865 public void widgetSelected(SelectionEvent e) {
866 DeviceCommandDialog dlg;
867 dlg = new DeviceCommandDialog(
868 "cat /data/logs/radio.4 /data/logs/radio.3"
869 + " /data/logs/radio.2 /data/logs/radio.1"
870 + " /data/logs/radio",
871 "radio-state.txt", shell);
872 dlg.open(mCurrentDevice);
876 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
877 logCatItem.setText("Run &logcat...");
878 logCatItem.addSelectionListener(new SelectionAdapter() {
880 public void widgetSelected(SelectionEvent e) {
881 DeviceCommandDialog dlg;
882 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
884 dlg.open(mCurrentDevice);
888 // configure Action items based on current state
889 deviceMenu.addMenuListener(new MenuAdapter() {
891 public void menuShown(MenuEvent e) {
892 boolean deviceEnabled = mCurrentDevice != null;
893 screenShotItem.setEnabled(deviceEnabled);
894 explorerItem.setEnabled(deviceEnabled);
895 processItem.setEnabled(deviceEnabled);
896 deviceStateItem.setEnabled(deviceEnabled);
897 appStateItem.setEnabled(deviceEnabled);
898 radioStateItem.setEnabled(deviceEnabled);
899 logCatItem.setEnabled(deviceEnabled);
903 // tell the shell to use this menu
904 shell.setMenuBar(menuBar);
908 * Create the widgets in the main application window. The basic layout is a
909 * two-panel sash, with a scrolling list of VMs on the left and detailed
910 * output for a single VM on the right.
912 private void createWidgets(final Shell shell) {
913 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
916 * Create three areas: tool bar, split panels, status line
918 shell.setLayout(new GridLayout(1, false));
921 final Composite panelArea = new Composite(shell, SWT.BORDER);
923 // make the panel area absorb all space
924 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
927 mStatusLine = new Label(shell, SWT.NONE);
929 // make status line extend all the way across
930 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
932 mStatusLine.setText("Initializing...");
935 * Configure the split-panel area.
937 final PreferenceStore prefs = PrefsDialog.getStore();
939 Composite topPanel = new Composite(panelArea, SWT.NONE);
940 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
941 sash.setBackground(darkGray);
942 Composite bottomPanel = new Composite(panelArea, SWT.NONE);
944 panelArea.setLayout(new FormLayout());
946 createTopPanel(topPanel, darkGray);
947 createBottomPanel(bottomPanel);
950 FormData data = new FormData();
951 data.top = new FormAttachment(0, 0);
952 data.bottom = new FormAttachment(sash, 0);
953 data.left = new FormAttachment(0, 0);
954 data.right = new FormAttachment(100, 0);
955 topPanel.setLayoutData(data);
957 final FormData sashData = new FormData();
958 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
959 sashData.top = new FormAttachment(0, prefs.getInt(
960 PREFERENCE_LOGSASH));
962 sashData.top = new FormAttachment(50,0); // 50% across
964 sashData.left = new FormAttachment(0, 0);
965 sashData.right = new FormAttachment(100, 0);
966 sash.setLayoutData(sashData);
968 data = new FormData();
969 data.top = new FormAttachment(sash, 0);
970 data.bottom = new FormAttachment(100, 0);
971 data.left = new FormAttachment(0, 0);
972 data.right = new FormAttachment(100, 0);
973 bottomPanel.setLayoutData(data);
975 // allow resizes, but cap at minPanelWidth
976 sash.addListener(SWT.Selection, new Listener() {
977 public void handleEvent(Event e) {
978 Rectangle sashRect = sash.getBounds();
979 Rectangle panelRect = panelArea.getClientArea();
980 int bottom = panelRect.height - sashRect.height - 100;
981 e.y = Math.max(Math.min(e.y, bottom), 100);
982 if (e.y != sashRect.y) {
983 sashData.top = new FormAttachment(0, e.y);
985 prefs.setValue(PREFERENCE_LOGSASH, e.y);
992 // add a global focus listener for all the tables
993 mTableListener = new TableFocusListener();
995 // now set up the listener in the various panels
996 mLogPanel.setTableFocusListener(mTableListener);
997 mEventLogPanel.setTableFocusListener(mTableListener);
998 for (TablePanel p : mPanels) {
1000 p.setTableFocusListener(mTableListener);
1004 mStatusLine.setText("");
1008 * Populate the tool bar.
1010 private void createDevicePanelToolBar(ToolBar toolBar) {
1011 Display display = toolBar.getDisplay();
1013 // add "show heap updates" button
1014 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1015 mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1016 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1017 mTBShowHeapUpdates.setToolTipText("Show heap updates");
1018 mTBShowHeapUpdates.setEnabled(false);
1019 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1021 public void widgetSelected(SelectionEvent e) {
1022 if (mCurrentClient != null) {
1023 // boolean status = ((ToolItem)e.item).getSelection();
1024 // invert previous state
1025 boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1026 mCurrentClient.setHeapUpdateEnabled(enable);
1028 e.doit = false; // this has no effect?
1033 // add "dump HPROF" button
1034 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1035 mTBDumpHprof.setToolTipText("Dump HPROF file");
1036 mTBDumpHprof.setEnabled(false);
1037 mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display,
1038 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1039 mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1041 public void widgetSelected(SelectionEvent e) {
1042 mDevicePanel.dumpHprof();
1044 // this will make sure the dump hprof button is disabled for the current selection
1045 // as the client is already dumping an hprof file
1050 // add "cause GC" button
1051 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1052 mTBCauseGc.setToolTipText("Cause an immediate GC");
1053 mTBCauseGc.setEnabled(false);
1054 mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display,
1055 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1056 mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1058 public void widgetSelected(SelectionEvent e) {
1059 mDevicePanel.forceGcOnSelectedClient();
1063 new ToolItem(toolBar, SWT.SEPARATOR);
1065 // add "show thread updates" button
1066 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1067 mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1068 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1069 mTBShowThreadUpdates.setToolTipText("Show thread updates");
1070 mTBShowThreadUpdates.setEnabled(false);
1071 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1073 public void widgetSelected(SelectionEvent e) {
1074 if (mCurrentClient != null) {
1075 // boolean status = ((ToolItem)e.item).getSelection();
1076 // invert previous state
1077 boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1079 mCurrentClient.setThreadUpdateEnabled(enable);
1081 e.doit = false; // this has no effect?
1086 // add a start/stop method tracing
1087 mTracingStartImage = mDdmUiLibLoader.loadImage(display,
1088 DevicePanel.ICON_TRACING_START,
1089 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1090 mTracingStopImage = mDdmUiLibLoader.loadImage(display,
1091 DevicePanel.ICON_TRACING_STOP,
1092 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1093 mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1094 mTBProfiling.setToolTipText("Start Method Profiling");
1095 mTBProfiling.setEnabled(false);
1096 mTBProfiling.setImage(mTracingStartImage);
1097 mTBProfiling.addSelectionListener(new SelectionAdapter() {
1099 public void widgetSelected(SelectionEvent e) {
1100 mDevicePanel.toggleMethodProfiling();
1104 new ToolItem(toolBar, SWT.SEPARATOR);
1106 // add "kill VM" button; need to make this visually distinct from
1107 // the status update buttons
1108 mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1109 mTBHalt.setToolTipText("Halt the target VM");
1110 mTBHalt.setEnabled(false);
1111 mTBHalt.setImage(mDdmUiLibLoader.loadImage(display,
1112 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1113 mTBHalt.addSelectionListener(new SelectionAdapter() {
1115 public void widgetSelected(SelectionEvent e) {
1116 mDevicePanel.killSelectedClient();
1123 private void createTopPanel(final Composite comp, Color darkGray) {
1124 final PreferenceStore prefs = PrefsDialog.getStore();
1126 comp.setLayout(new FormLayout());
1128 Composite leftPanel = new Composite(comp, SWT.NONE);
1129 final Sash sash = new Sash(comp, SWT.VERTICAL);
1130 sash.setBackground(darkGray);
1131 Composite rightPanel = new Composite(comp, SWT.NONE);
1133 createLeftPanel(leftPanel);
1134 createRightPanel(rightPanel);
1136 FormData data = new FormData();
1137 data.top = new FormAttachment(0, 0);
1138 data.bottom = new FormAttachment(100, 0);
1139 data.left = new FormAttachment(0, 0);
1140 data.right = new FormAttachment(sash, 0);
1141 leftPanel.setLayoutData(data);
1143 final FormData sashData = new FormData();
1144 sashData.top = new FormAttachment(0, 0);
1145 sashData.bottom = new FormAttachment(100, 0);
1146 if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1147 sashData.left = new FormAttachment(0, prefs.getInt(
1150 // position the sash 380 from the right instead of x% (done by using
1151 // FormAttachment(x, 0)) in order to keep the sash at the same
1153 // from the left when the window is resized.
1154 // 380px is just enough to display the left table with no horizontal
1155 // scrollbar with the default font.
1156 sashData.left = new FormAttachment(0, 380);
1158 sash.setLayoutData(sashData);
1160 data = new FormData();
1161 data.top = new FormAttachment(0, 0);
1162 data.bottom = new FormAttachment(100, 0);
1163 data.left = new FormAttachment(sash, 0);
1164 data.right = new FormAttachment(100, 0);
1165 rightPanel.setLayoutData(data);
1167 final int minPanelWidth = 60;
1169 // allow resizes, but cap at minPanelWidth
1170 sash.addListener(SWT.Selection, new Listener() {
1171 public void handleEvent(Event e) {
1172 Rectangle sashRect = sash.getBounds();
1173 Rectangle panelRect = comp.getClientArea();
1174 int right = panelRect.width - sashRect.width - minPanelWidth;
1175 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1176 if (e.x != sashRect.x) {
1177 sashData.left = new FormAttachment(0, e.x);
1178 if (prefs != null) {
1179 prefs.setValue(PREFERENCE_SASH, e.x);
1187 private void createBottomPanel(final Composite comp) {
1188 final PreferenceStore prefs = PrefsDialog.getStore();
1191 Display display = comp.getDisplay();
1192 mClipboard = new Clipboard(display);
1194 LogColors colors = new LogColors();
1196 colors.infoColor = new Color(display, 0, 127, 0);
1197 colors.debugColor = new Color(display, 0, 0, 127);
1198 colors.errorColor = new Color(display, 255, 0, 0);
1199 colors.warningColor = new Color(display, 255, 127, 0);
1200 colors.verboseColor = new Color(display, 0, 0, 0);
1202 // set the preferences names
1203 LogPanel.PREFS_TIME = PREFS_COL_TIME;
1204 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1205 LogPanel.PREFS_PID = PREFS_COL_PID;
1206 LogPanel.PREFS_TAG = PREFS_COL_TAG;
1207 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1209 comp.setLayout(new GridLayout(1, false));
1211 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1213 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1214 mCreateFilterAction.item.setToolTipText("Create Filter");
1215 mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1216 "add.png", //$NON-NLS-1$
1217 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1218 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1220 public void widgetSelected(SelectionEvent e) {
1221 mLogPanel.addFilter();
1225 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1226 mEditFilterAction.item.setToolTipText("Edit Filter");
1227 mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1228 "edit.png", //$NON-NLS-1$
1229 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1230 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1232 public void widgetSelected(SelectionEvent e) {
1233 mLogPanel.editFilter();
1237 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1238 mDeleteFilterAction.item.setToolTipText("Delete Filter");
1239 mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1240 "delete.png", //$NON-NLS-1$
1241 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1242 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1244 public void widgetSelected(SelectionEvent e) {
1245 mLogPanel.deleteFilter();
1250 new ToolItem(toolBar, SWT.SEPARATOR);
1252 LogLevel[] levels = LogLevel.values();
1253 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1254 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1255 String name = levels[i].getStringValue();
1256 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1257 mLogLevelActions[i] = newAction;
1258 //newAction.item.setText(name);
1259 newAction.item.addSelectionListener(new SelectionAdapter() {
1261 public void widgetSelected(SelectionEvent e) {
1262 // disable the other actions and record current index
1263 for (int k = 0 ; k < mLogLevelActions.length; k++) {
1264 ToolItemAction a = mLogLevelActions[k];
1265 if (a == newAction) {
1268 // set the log level
1269 mLogPanel.setCurrentFilterLogLevel(k+2);
1271 a.setChecked(false);
1277 newAction.item.setToolTipText(name);
1278 newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1280 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1283 new ToolItem(toolBar, SWT.SEPARATOR);
1285 mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1286 mClearAction.item.setToolTipText("Clear Log");
1288 mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1289 "clear.png", //$NON-NLS-1$
1290 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1291 mClearAction.item.addSelectionListener(new SelectionAdapter() {
1293 public void widgetSelected(SelectionEvent e) {
1298 new ToolItem(toolBar, SWT.SEPARATOR);
1300 mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1301 mExportAction.item.setToolTipText("Export Selection As Text...");
1302 mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1303 "save.png", //$NON-NLS-1$
1304 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1305 mExportAction.item.addSelectionListener(new SelectionAdapter() {
1307 public void widgetSelected(SelectionEvent e) {
1315 // now create the log view
1316 mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
1318 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1320 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1321 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1322 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1325 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1326 if (fontStr != null) {
1328 FontData fdat = new FontData(fontStr);
1329 mLogPanel.setFont(new Font(display, fdat));
1330 } catch (IllegalArgumentException e) {
1331 // Looks like fontStr isn't a valid font representation.
1332 // We do nothing in this case, the logcat view will use the default font.
1333 } catch (SWTError e2) {
1334 // Looks like the Font() constructor failed.
1335 // We do nothing in this case, the logcat view will use the default font.
1339 mLogPanel.createPanel(comp);
1341 // and start the logcat
1342 mLogPanel.startLogCat(mCurrentDevice);
1346 * Create the contents of the left panel: a table of VMs.
1348 private void createLeftPanel(final Composite comp) {
1349 comp.setLayout(new GridLayout(1, false));
1350 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1351 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1352 createDevicePanelToolBar(toolBar);
1354 Composite c = new Composite(comp, SWT.NONE);
1355 c.setLayoutData(new GridData(GridData.FILL_BOTH));
1357 mDevicePanel = new DevicePanel(true /* showPorts */);
1358 mDevicePanel.createPanel(c);
1360 // add ourselves to the device panel selection listener
1361 mDevicePanel.addSelectionListener(this);
1365 * Create the contents of the right panel: tabs with VM information.
1367 private void createRightPanel(final Composite comp) {
1369 TabFolder tabFolder;
1371 comp.setLayout(new FillLayout());
1373 tabFolder = new TabFolder(comp, SWT.NONE);
1375 for (int i = 0; i < mPanels.length; i++) {
1376 if (mPanels[i] != null) {
1377 item = new TabItem(tabFolder, SWT.NONE);
1378 item.setText(mPanelNames[i]);
1379 item.setToolTipText(mPanelTips[i]);
1380 item.setControl(mPanels[i].createPanel(tabFolder));
1384 // add the emulator control panel to the folders.
1385 item = new TabItem(tabFolder, SWT.NONE);
1386 item.setText("Emulator Control");
1387 item.setToolTipText("Emulator Control Panel");
1388 mEmulatorPanel = new EmulatorControlPanel();
1389 item.setControl(mEmulatorPanel.createPanel(tabFolder));
1391 // add the event log panel to the folders.
1392 item = new TabItem(tabFolder, SWT.NONE);
1393 item.setText("Event Log");
1394 item.setToolTipText("Event Log");
1396 // create the composite that will hold the toolbar and the event log panel.
1397 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1398 item.setControl(eventLogTopComposite);
1399 eventLogTopComposite.setLayout(new GridLayout(1, false));
1401 // create the toolbar and the actions
1402 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1403 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1405 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1406 optionsAction.item.setToolTipText("Opens the options panel");
1407 optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1408 "edit.png", //$NON-NLS-1$
1409 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1411 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1412 clearAction.item.setToolTipText("Clears the event log");
1413 clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1414 "clear.png", //$NON-NLS-1$
1415 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1417 new ToolItem(toolbar, SWT.SEPARATOR);
1419 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1420 saveAction.item.setToolTipText("Saves the event log");
1421 saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1422 "save.png", //$NON-NLS-1$
1423 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1425 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1426 loadAction.item.setToolTipText("Loads an event log");
1427 loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1428 "load.png", //$NON-NLS-1$
1429 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1431 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1432 importBugAction.item.setToolTipText("Imports a bug report");
1433 importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1434 "importBug.png", //$NON-NLS-1$
1435 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1437 // create the event log panel
1438 mEventLogPanel = new EventLogPanel();
1440 // set the external actions
1441 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1445 mEventLogPanel.createPanel(eventLogTopComposite);
1448 private void createFileExplorer() {
1449 if (mExplorer == null) {
1450 mExplorerShell = new Shell(mDisplay);
1453 mExplorerShell.setLayout(new GridLayout(1, false));
1456 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1457 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1459 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1460 pullAction.item.setToolTipText("Pull File from Device");
1461 Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1462 if (image != null) {
1463 pullAction.item.setImage(image);
1465 // this is for debugging purpose when the icon is missing
1466 pullAction.item.setText("Pull"); //$NON-NLS-1$
1469 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1470 pushAction.item.setToolTipText("Push file onto Device");
1471 image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1472 if (image != null) {
1473 pushAction.item.setImage(image);
1475 // this is for debugging purpose when the icon is missing
1476 pushAction.item.setText("Push"); //$NON-NLS-1$
1479 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1480 deleteAction.item.setToolTipText("Delete");
1481 image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1482 if (image != null) {
1483 deleteAction.item.setImage(image);
1485 // this is for debugging purpose when the icon is missing
1486 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1489 ToolItemAction createNewFolderAction = new ToolItemAction(toolBar, SWT.PUSH);
1490 createNewFolderAction.item.setToolTipText("New Folder");
1491 image = mDdmUiLibLoader.loadImage("add.png", mDisplay); //$NON-NLS-1$
1492 if (image != null) {
1493 createNewFolderAction.item.setImage(image);
1495 // this is for debugging purpose when the icon is missing
1496 createNewFolderAction.item.setText("New Folder"); //$NON-NLS-1$
1500 mExplorer = new DeviceExplorer();
1501 mExplorer.setActions(pushAction, pullAction, deleteAction, createNewFolderAction);
1503 pullAction.item.addSelectionListener(new SelectionAdapter() {
1505 public void widgetSelected(SelectionEvent e) {
1506 mExplorer.pullSelection();
1509 pullAction.setEnabled(false);
1511 pushAction.item.addSelectionListener(new SelectionAdapter() {
1513 public void widgetSelected(SelectionEvent e) {
1514 mExplorer.pushIntoSelection();
1517 pushAction.setEnabled(false);
1519 deleteAction.item.addSelectionListener(new SelectionAdapter() {
1521 public void widgetSelected(SelectionEvent e) {
1522 mExplorer.deleteSelection();
1525 deleteAction.setEnabled(false);
1527 createNewFolderAction.item.addSelectionListener(new SelectionAdapter() {
1529 public void widgetSelected(SelectionEvent e) {
1530 mExplorer.createNewFolderInSelection();
1533 createNewFolderAction.setEnabled(false);
1535 Composite parent = new Composite(mExplorerShell, SWT.NONE);
1536 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1538 mExplorer.createPanel(parent);
1539 mExplorer.switchDevice(mCurrentDevice);
1541 mExplorerShell.addShellListener(new ShellListener() {
1542 public void shellActivated(ShellEvent e) {
1546 public void shellClosed(ShellEvent e) {
1548 mExplorerShell = null;
1551 public void shellDeactivated(ShellEvent e) {
1555 public void shellDeiconified(ShellEvent e) {
1559 public void shellIconified(ShellEvent e) {
1564 mExplorerShell.pack();
1565 setExplorerSizeAndPosition(mExplorerShell);
1566 mExplorerShell.open();
1568 if (mExplorerShell != null) {
1569 mExplorerShell.forceActive();
1575 * Set the status line. TODO: make this a stack, so we can safely have
1576 * multiple things trying to set it all at once. Also specify an expiration?
1578 public void setStatusLine(final String str) {
1580 mDisplay.asyncExec(new Runnable() {
1582 doSetStatusLine(str);
1585 } catch (SWTException swte) {
1586 if (!mDisplay.isDisposed())
1591 private void doSetStatusLine(String str) {
1592 if (mStatusLine.isDisposed())
1595 if (!mStatusLine.getText().equals(str)) {
1596 mStatusLine.setText(str);
1598 // try { Thread.sleep(100); }
1599 // catch (InterruptedException ie) {}
1603 public void displayError(final String msg) {
1605 mDisplay.syncExec(new Runnable() {
1607 MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1611 } catch (SWTException swte) {
1612 if (!mDisplay.isDisposed())
1617 private void enableButtons() {
1618 if (mCurrentClient != null) {
1619 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1620 mTBShowThreadUpdates.setEnabled(true);
1621 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1622 mTBShowHeapUpdates.setEnabled(true);
1623 mTBHalt.setEnabled(true);
1624 mTBCauseGc.setEnabled(true);
1626 ClientData data = mCurrentClient.getClientData();
1628 if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1629 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1630 mTBDumpHprof.setToolTipText("Dump HPROF file");
1632 mTBDumpHprof.setEnabled(false);
1633 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1636 if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1637 mTBProfiling.setEnabled(true);
1638 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1639 mTBProfiling.setToolTipText("Stop Method Profiling");
1640 mTBProfiling.setImage(mTracingStopImage);
1642 mTBProfiling.setToolTipText("Start Method Profiling");
1643 mTBProfiling.setImage(mTracingStartImage);
1646 mTBProfiling.setEnabled(false);
1647 mTBProfiling.setImage(mTracingStartImage);
1648 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1651 // list is empty, disable these
1652 mTBShowThreadUpdates.setSelection(false);
1653 mTBShowThreadUpdates.setEnabled(false);
1654 mTBShowHeapUpdates.setSelection(false);
1655 mTBShowHeapUpdates.setEnabled(false);
1656 mTBHalt.setEnabled(false);
1657 mTBCauseGc.setEnabled(false);
1659 mTBDumpHprof.setEnabled(false);
1660 mTBDumpHprof.setToolTipText("Dump HPROF file");
1662 mTBProfiling.setEnabled(false);
1663 mTBProfiling.setImage(mTracingStartImage);
1664 mTBProfiling.setToolTipText("Start Method Profiling");
1669 * Sent when a new {@link IDevice} and {@link Client} are selected.
1670 * @param selectedDevice the selected device. If null, no devices are selected.
1671 * @param selectedClient The selected client. If null, no clients are selected.
1673 * @see IUiSelectionListener
1675 public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1676 if (mCurrentDevice != selectedDevice) {
1677 mCurrentDevice = selectedDevice;
1678 for (TablePanel panel : mPanels) {
1679 if (panel != null) {
1680 panel.deviceSelected(mCurrentDevice);
1684 mEmulatorPanel.deviceSelected(mCurrentDevice);
1685 mLogPanel.deviceSelected(mCurrentDevice);
1686 if (mEventLogPanel != null) {
1687 mEventLogPanel.deviceSelected(mCurrentDevice);
1690 if (mExplorer != null) {
1691 mExplorer.switchDevice(mCurrentDevice);
1695 if (mCurrentClient != selectedClient) {
1696 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1697 mCurrentClient = selectedClient;
1698 for (TablePanel panel : mPanels) {
1699 if (panel != null) {
1700 panel.clientSelected(mCurrentClient);
1708 public void clientChanged(Client client, int changeMask) {
1709 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1710 Client.CHANGE_METHOD_PROFILING_STATUS) {
1711 if (mCurrentClient == client) {
1712 mDisplay.asyncExec(new Runnable() {
1714 // force refresh of the button enabled state.