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.LogCatPanel;
50 import com.android.ddmuilib.logcat.LogCatReceiver;
51 import com.android.ddmuilib.logcat.LogColors;
52 import com.android.ddmuilib.logcat.LogFilter;
53 import com.android.ddmuilib.logcat.LogPanel;
54 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
55 import com.android.menubar.IMenuBarCallback;
56 import com.android.menubar.IMenuBarEnhancer;
57 import com.android.menubar.IMenuBarEnhancer.MenuBarMode;
58 import com.android.menubar.MenuBarEnhancer;
60 import org.eclipse.jface.dialogs.MessageDialog;
61 import org.eclipse.jface.preference.IPreferenceStore;
62 import org.eclipse.jface.preference.PreferenceStore;
63 import org.eclipse.swt.SWT;
64 import org.eclipse.swt.SWTError;
65 import org.eclipse.swt.SWTException;
66 import org.eclipse.swt.dnd.Clipboard;
67 import org.eclipse.swt.events.ControlEvent;
68 import org.eclipse.swt.events.ControlListener;
69 import org.eclipse.swt.events.MenuAdapter;
70 import org.eclipse.swt.events.MenuEvent;
71 import org.eclipse.swt.events.SelectionAdapter;
72 import org.eclipse.swt.events.SelectionEvent;
73 import org.eclipse.swt.events.ShellEvent;
74 import org.eclipse.swt.events.ShellListener;
75 import org.eclipse.swt.graphics.Color;
76 import org.eclipse.swt.graphics.Font;
77 import org.eclipse.swt.graphics.FontData;
78 import org.eclipse.swt.graphics.Image;
79 import org.eclipse.swt.graphics.Rectangle;
80 import org.eclipse.swt.layout.FillLayout;
81 import org.eclipse.swt.layout.FormAttachment;
82 import org.eclipse.swt.layout.FormData;
83 import org.eclipse.swt.layout.FormLayout;
84 import org.eclipse.swt.layout.GridData;
85 import org.eclipse.swt.layout.GridLayout;
86 import org.eclipse.swt.widgets.Composite;
87 import org.eclipse.swt.widgets.Display;
88 import org.eclipse.swt.widgets.Event;
89 import org.eclipse.swt.widgets.Label;
90 import org.eclipse.swt.widgets.Listener;
91 import org.eclipse.swt.widgets.Menu;
92 import org.eclipse.swt.widgets.MenuItem;
93 import org.eclipse.swt.widgets.Sash;
94 import org.eclipse.swt.widgets.Shell;
95 import org.eclipse.swt.widgets.TabFolder;
96 import org.eclipse.swt.widgets.TabItem;
97 import org.eclipse.swt.widgets.ToolBar;
98 import org.eclipse.swt.widgets.ToolItem;
101 import java.util.ArrayList;
104 * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
105 * SWT application. So this class mainly builds the ui, and manages communication between the panels
106 * when {@link IDevice} / {@link Client} selection changes.
108 public class UIThread implements IUiSelectionListener, IClientChangeListener {
109 private static final String APP_NAME = "DDMS";
112 * UI tab panel definitions. The constants here must match up with the array
113 * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
116 public static final int PANEL_CLIENT_LIST = -1;
118 public static final int PANEL_INFO = 0;
120 public static final int PANEL_THREAD = 1;
122 public static final int PANEL_HEAP = 2;
124 public static final int PANEL_NATIVE_HEAP = 3;
126 private static final int PANEL_ALLOCATIONS = 4;
128 private static final int PANEL_SYSINFO = 5;
130 private static final int PANEL_COUNT = 6;
132 /** Content is setup in the constructor */
133 private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
135 private static final String[] mPanelNames = new String[] {
136 "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
139 private static final String[] mPanelTips = new String[] {
140 "Client information", "Thread status", "VM heap status",
141 "Native heap status", "Allocation Tracker", "Sysinfo graphs"
144 private static final String PREFERENCE_LOGSASH =
145 "logSashLocation"; //$NON-NLS-1$
146 private static final String PREFERENCE_SASH =
147 "sashLocation"; //$NON-NLS-1$
149 private static final String PREFS_COL_TIME =
150 "logcat.time"; //$NON-NLS-1$
151 private static final String PREFS_COL_LEVEL =
152 "logcat.level"; //$NON-NLS-1$
153 private static final String PREFS_COL_PID =
154 "logcat.pid"; //$NON-NLS-1$
155 private static final String PREFS_COL_TAG =
156 "logcat.tag"; //$NON-NLS-1$
157 private static final String PREFS_COL_MESSAGE =
158 "logcat.message"; //$NON-NLS-1$
160 private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
162 // singleton instance
163 private static UIThread mInstance = new UIThread();
166 private Display mDisplay;
168 // the table we show in the left-hand pane
169 private DevicePanel mDevicePanel;
171 private IDevice mCurrentDevice = null;
172 private Client mCurrentClient = null;
174 // status line at the bottom of the app window
175 private Label mStatusLine;
177 // some toolbar items we need to update
178 private ToolItem mTBShowThreadUpdates;
179 private ToolItem mTBShowHeapUpdates;
180 private ToolItem mTBHalt;
181 private ToolItem mTBCauseGc;
182 private ToolItem mTBDumpHprof;
183 private ToolItem mTBProfiling;
185 private final class FilterStorage implements ILogFilterStorageManager {
187 public LogFilter[] getFilterFromStore() {
188 String filterPrefs = PrefsDialog.getStore().getString(
191 // split in a string per filter
192 String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
194 ArrayList<LogFilter> list =
195 new ArrayList<LogFilter>(filters.length);
197 for (String f : filters) {
198 if (f.length() > 0) {
199 LogFilter logFilter = new LogFilter();
200 if (logFilter.loadFromString(f)) {
206 return list.toArray(new LogFilter[list.size()]);
209 public void saveFilters(LogFilter[] filters) {
210 StringBuilder sb = new StringBuilder();
211 for (LogFilter f : filters) {
212 String filterString = f.toString();
213 sb.append(filterString);
217 PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
220 public boolean requiresDefaultFilter() {
227 * Flag to indicate whether to use the old or the new logcat view. This is a
228 * temporary workaround that will be removed once the new view is complete.
230 private static final String USE_NEW_LOGCAT_VIEW =
231 System.getenv("ANDROID_USE_NEW_LOGCAT_VIEW");
232 private boolean useOldLogCatView() {
233 return USE_NEW_LOGCAT_VIEW == null;
236 private LogPanel mLogPanel; /* only valid when useOldLogCatView() == true */
237 private LogCatPanel mLogCatPanel; /* only valid when useOldLogCatView() == false */
239 private ToolItemAction mCreateFilterAction;
240 private ToolItemAction mDeleteFilterAction;
241 private ToolItemAction mEditFilterAction;
242 private ToolItemAction mExportAction;
243 private ToolItemAction mClearAction;
245 private ToolItemAction[] mLogLevelActions;
246 private String[] mLogLevelIcons = {
247 "v.png", //$NON-NLS-1S
248 "d.png", //$NON-NLS-1S
249 "i.png", //$NON-NLS-1S
250 "w.png", //$NON-NLS-1S
251 "e.png", //$NON-NLS-1S
254 protected Clipboard mClipboard;
256 private MenuItem mCopyMenuItem;
258 private MenuItem mSelectAllMenuItem;
260 private TableFocusListener mTableListener;
262 private DeviceExplorer mExplorer = null;
263 private Shell mExplorerShell = null;
265 private EmulatorControlPanel mEmulatorPanel;
267 private EventLogPanel mEventLogPanel;
269 private Image mTracingStartImage;
271 private Image mTracingStopImage;
273 private ImageLoader mDdmUiLibLoader;
275 private class TableFocusListener implements ITableFocusListener {
277 private IFocusedTableActivator mCurrentActivator;
279 public void focusGained(IFocusedTableActivator activator) {
280 mCurrentActivator = activator;
281 if (mCopyMenuItem.isDisposed() == false) {
282 mCopyMenuItem.setEnabled(true);
283 mSelectAllMenuItem.setEnabled(true);
287 public void focusLost(IFocusedTableActivator activator) {
288 // if we move from one table to another, it's unclear
289 // if the old table lose its focus before the new
290 // one gets the focus, so we need to check.
291 if (activator == mCurrentActivator) {
293 if (mCopyMenuItem.isDisposed() == false) {
294 mCopyMenuItem.setEnabled(false);
295 mSelectAllMenuItem.setEnabled(false);
300 public void copy(Clipboard clipboard) {
301 if (mCurrentActivator != null) {
302 mCurrentActivator.copy(clipboard);
306 public void selectAll() {
307 if (mCurrentActivator != null) {
308 mCurrentActivator.selectAll();
314 * Handler for HPROF dumps.
315 * This will always prompt the user to save the HPROF file.
317 private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
319 public HProfHandler(Shell parentShell) {
323 public void onEndFailure(final Client client, final String message) {
324 mDisplay.asyncExec(new Runnable() {
327 displayErrorFromUiThread(
328 "Unable to create HPROF file for application '%1$s'\n\n%2$s" +
329 "Check logcat for more information.",
330 client.getClientData().getClientDescription(),
331 message != null ? message + "\n\n" : "");
333 // this will make sure the dump hprof button is re-enabled for the
334 // current selection. as the client is finished dumping an hprof file
341 public void onSuccess(final String remoteFilePath, final Client client) {
342 mDisplay.asyncExec(new Runnable() {
344 final IDevice device = client.getDevice();
346 // get the sync service to pull the HPROF file
347 final SyncService sync = client.getDevice().getSyncService();
350 client.getClientData().getClientDescription() + ".hprof",
351 remoteFilePath, "Save HPROF file");
353 displayErrorFromUiThread(
354 "Unable to download HPROF file from device '%1$s'.",
355 device.getSerialNumber());
357 } catch (SyncException e) {
358 if (e.wasCanceled() == false) {
359 displayErrorFromUiThread(
360 "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
361 device.getSerialNumber(), e.getMessage());
363 } catch (Exception e) {
364 displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.",
365 device.getSerialNumber());
368 // this will make sure the dump hprof button is re-enabled for the
369 // current selection. as the client is finished dumping an hprof file
376 public void onSuccess(final byte[] data, final Client client) {
377 mDisplay.asyncExec(new Runnable() {
379 promptAndSave(client.getClientData().getClientDescription() + ".hprof", data,
386 protected String getDialogTitle() {
387 return "HPROF Error";
393 * Generic constructor.
396 mPanels[PANEL_INFO] = new InfoPanel();
397 mPanels[PANEL_THREAD] = new ThreadPanel();
398 mPanels[PANEL_HEAP] = new HeapPanel();
399 if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
400 mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
402 mPanels[PANEL_NATIVE_HEAP] = null;
404 mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
405 mPanels[PANEL_SYSINFO] = new SysinfoPanel();
409 * Get singleton instance of the UI thread.
411 public static UIThread getInstance() {
416 * Return the Display. Don't try this unless you're in the UI thread.
418 public Display getDisplay() {
422 public void asyncExec(Runnable r) {
423 if (mDisplay != null && mDisplay.isDisposed() == false) {
424 mDisplay.asyncExec(r);
428 /** returns the IPreferenceStore */
429 public IPreferenceStore getStore() {
430 return PrefsDialog.getStore();
434 * Create SWT objects and drive the user interface event loop.
435 * @param ddmsParentLocation location of the folder that contains ddms.
437 public void runUI(String ddmsParentLocation) {
438 Display.setAppName(APP_NAME);
439 mDisplay = new Display();
440 final Shell shell = new Shell(mDisplay);
442 // create the image loaders for DDMS and DDMUILIB
443 mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader();
445 shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay,
446 "ddms-128.png", //$NON-NLS-1$
449 Log.setLogOutput(new ILogOutput() {
450 public void printAndPromptLog(final LogLevel logLevel, final String tag,
451 final String message) {
452 Log.printLog(logLevel, tag, message);
453 // dialog box only run in UI thread..
454 mDisplay.asyncExec(new Runnable() {
456 Shell activeShell = mDisplay.getActiveShell();
457 if (logLevel == LogLevel.ERROR) {
458 MessageDialog.openError(activeShell, tag, message);
460 MessageDialog.openWarning(activeShell, tag, message);
466 public void printLog(LogLevel logLevel, String tag, String message) {
467 Log.printLog(logLevel, tag, message);
471 // set the handler for hprof dump
472 ClientData.setHprofDumpHandler(new HProfHandler(shell));
473 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
475 // [try to] ensure ADB is running
476 // in the new SDK, adb is in the platform-tools, but when run from the command line
477 // in the Android source tree, then adb is next to ddms.
479 if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
480 // check if there's a platform-tools folder
481 File platformTools = new File(new File(ddmsParentLocation).getParent(),
482 "platform-tools"); //$NON-NLS-1$
483 if (platformTools.isDirectory()) {
484 adbLocation = platformTools.getAbsolutePath() + File.separator + "adb"; //$NON-NLS-1$
486 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
489 adbLocation = "adb"; //$NON-NLS-1$
492 AndroidDebugBridge.init(true /* debugger support */);
493 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
495 // we need to listen to client change to be notified of client status (profiling) change
496 AndroidDebugBridge.addClientChangeListener(this);
498 shell.setText("Dalvik Debug Monitor");
499 setConfirmClose(shell);
501 createWidgets(shell);
504 setSizeAndPosition(shell);
507 Log.d("ddms", "UI is up");
509 while (!shell.isDisposed()) {
510 if (!mDisplay.readAndDispatch())
513 if (useOldLogCatView()) {
514 mLogPanel.stopLogCat(true);
517 mDevicePanel.dispose();
518 for (TablePanel panel : mPanels) {
524 ImageLoader.dispose();
527 Log.d("ddms", "UI is down");
531 * Set the size and position of the main window from the preference, and
532 * setup listeners for control events (resize/move of the window)
534 private void setSizeAndPosition(final Shell shell) {
535 shell.setMinimumSize(400, 200);
537 // get the x/y and w/h from the prefs
538 PreferenceStore prefs = PrefsDialog.getStore();
539 int x = prefs.getInt(PrefsDialog.SHELL_X);
540 int y = prefs.getInt(PrefsDialog.SHELL_Y);
541 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
542 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
544 // check that we're not out of the display area
545 Rectangle rect = mDisplay.getClientArea();
546 // first check the width/height
547 if (w > rect.width) {
549 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
551 if (h > rect.height) {
553 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
555 // then check x. Make sure the left corner is in the screen
558 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
559 } else if (x >= rect.x + rect.width) {
560 x = rect.x + rect.width - w;
561 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
563 // then check y. Make sure the left corner is in the screen
566 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
567 } else if (y >= rect.y + rect.height) {
568 y = rect.y + rect.height - h;
569 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
572 // now we can set the location/size
573 shell.setBounds(x, y, w, h);
575 // add listener for resize/move
576 shell.addControlListener(new ControlListener() {
577 public void controlMoved(ControlEvent e) {
579 Rectangle controlBounds = shell.getBounds();
580 // store in pref file
581 PreferenceStore currentPrefs = PrefsDialog.getStore();
582 currentPrefs.setValue(PrefsDialog.SHELL_X, controlBounds.x);
583 currentPrefs.setValue(PrefsDialog.SHELL_Y, controlBounds.y);
586 public void controlResized(ControlEvent e) {
588 Rectangle controlBounds = shell.getBounds();
589 // store in pref file
590 PreferenceStore currentPrefs = PrefsDialog.getStore();
591 currentPrefs.setValue(PrefsDialog.SHELL_WIDTH, controlBounds.width);
592 currentPrefs.setValue(PrefsDialog.SHELL_HEIGHT, controlBounds.height);
598 * Set the size and position of the file explorer window from the
599 * preference, and setup listeners for control events (resize/move of
602 private void setExplorerSizeAndPosition(final Shell shell) {
603 shell.setMinimumSize(400, 200);
605 // get the x/y and w/h from the prefs
606 PreferenceStore prefs = PrefsDialog.getStore();
607 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
608 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
609 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
610 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
612 // check that we're not out of the display area
613 Rectangle rect = mDisplay.getClientArea();
614 // first check the width/height
615 if (w > rect.width) {
617 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
619 if (h > rect.height) {
621 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
623 // then check x. Make sure the left corner is in the screen
626 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
627 } else if (x >= rect.x + rect.width) {
628 x = rect.x + rect.width - w;
629 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
631 // then check y. Make sure the left corner is in the screen
634 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
635 } else if (y >= rect.y + rect.height) {
636 y = rect.y + rect.height - h;
637 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
640 // now we can set the location/size
641 shell.setBounds(x, y, w, h);
643 // add listener for resize/move
644 shell.addControlListener(new ControlListener() {
645 public void controlMoved(ControlEvent e) {
647 Rectangle controlBounds = shell.getBounds();
648 // store in pref file
649 PreferenceStore currentPrefs = PrefsDialog.getStore();
650 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_X, controlBounds.x);
651 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, controlBounds.y);
654 public void controlResized(ControlEvent e) {
656 Rectangle controlBounds = shell.getBounds();
657 // store in pref file
658 PreferenceStore currentPrefs = PrefsDialog.getStore();
659 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, controlBounds.width);
660 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, controlBounds.height);
666 * Set the confirm-before-close dialog.
668 private void setConfirmClose(final Shell shell) {
669 // Note: there was some commented out code to display a confirmation box
670 // when closing. The feature seems unnecessary and the code was not being
671 // used, so it has been removed.
675 * Create the menu bar and items.
677 private void createMenus(final Shell shell) {
679 Menu menuBar = new Menu(shell, SWT.BAR);
681 // create top-level items
682 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
683 fileItem.setText("&File");
684 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
685 editItem.setText("&Edit");
686 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
687 actionItem.setText("&Actions");
688 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
689 deviceItem.setText("&Device");
691 // create top-level menus
692 Menu fileMenu = new Menu(menuBar);
693 fileItem.setMenu(fileMenu);
694 Menu editMenu = new Menu(menuBar);
695 editItem.setMenu(editMenu);
696 Menu actionMenu = new Menu(menuBar);
697 actionItem.setMenu(actionMenu);
698 Menu deviceMenu = new Menu(menuBar);
699 deviceItem.setMenu(deviceMenu);
703 // create File menu items
704 item = new MenuItem(fileMenu, SWT.NONE);
705 item.setText("&Static Port Configuration...");
706 item.addSelectionListener(new SelectionAdapter() {
708 public void widgetSelected(SelectionEvent e) {
709 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
714 IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenu(APP_NAME, fileMenu,
715 new IMenuBarCallback() {
716 public void printError(String format, Object... args) {
717 Log.e("DDMS Menu Bar", String.format(format, args));
720 public void onPreferencesMenuSelected() {
721 PrefsDialog.run(shell);
724 public void onAboutMenuSelected() {
725 AboutDialog dlg = new AboutDialog(shell);
730 if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
731 new MenuItem(fileMenu, SWT.SEPARATOR);
733 item = new MenuItem(fileMenu, SWT.NONE);
734 item.setText("E&xit\tCtrl-Q");
735 item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
736 item.addSelectionListener(new SelectionAdapter() {
738 public void widgetSelected(SelectionEvent e) {
744 // create edit menu items
745 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
746 mCopyMenuItem.setText("&Copy\tCtrl-C");
747 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
748 mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
750 public void widgetSelected(SelectionEvent e) {
751 mTableListener.copy(mClipboard);
755 new MenuItem(editMenu, SWT.SEPARATOR);
757 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
758 mSelectAllMenuItem.setText("Select &All\tCtrl-A");
759 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
760 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
762 public void widgetSelected(SelectionEvent e) {
763 mTableListener.selectAll();
767 // create Action menu items
768 // TODO: this should come with a confirmation dialog
769 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
770 actionHaltItem.setText("&Halt VM");
771 actionHaltItem.addSelectionListener(new SelectionAdapter() {
773 public void widgetSelected(SelectionEvent e) {
774 mDevicePanel.killSelectedClient();
778 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
779 actionCauseGcItem.setText("Cause &GC");
780 actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
782 public void widgetSelected(SelectionEvent e) {
783 mDevicePanel.forceGcOnSelectedClient();
787 final MenuItem actionResetAdb = new MenuItem(actionMenu, SWT.NONE);
788 actionResetAdb.setText("&Reset adb");
789 actionResetAdb.addSelectionListener(new SelectionAdapter() {
791 public void widgetSelected(SelectionEvent e) {
792 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
793 if (bridge != null) {
799 // configure Action items based on current state
800 actionMenu.addMenuListener(new MenuAdapter() {
802 public void menuShown(MenuEvent e) {
803 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
804 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
805 actionResetAdb.setEnabled(true);
809 // create Device menu items
810 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
812 // The \tCtrl-S "keybinding text" here isn't right for the Mac - but
813 // it's stripped out and replaced by the proper keyboard accelerator
814 // text (e.g. the unicode symbol for the command key + S) anyway
815 // so it's fine to leave it there for the other platforms.
816 screenShotItem.setText("&Screen capture...\tCtrl-S");
817 screenShotItem.setAccelerator('S' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
818 screenShotItem.addSelectionListener(new SelectionAdapter() {
820 public void widgetSelected(SelectionEvent e) {
821 if (mCurrentDevice != null) {
822 ScreenShotDialog dlg = new ScreenShotDialog(shell);
823 dlg.open(mCurrentDevice);
828 new MenuItem(deviceMenu, SWT.SEPARATOR);
830 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
831 explorerItem.setText("File Explorer...");
832 explorerItem.addSelectionListener(new SelectionAdapter() {
834 public void widgetSelected(SelectionEvent e) {
835 createFileExplorer();
839 new MenuItem(deviceMenu, SWT.SEPARATOR);
841 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
842 processItem.setText("Show &process status...");
843 processItem.addSelectionListener(new SelectionAdapter() {
845 public void widgetSelected(SelectionEvent e) {
846 DeviceCommandDialog dlg;
847 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
848 dlg.open(mCurrentDevice);
852 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
853 deviceStateItem.setText("Dump &device state...");
854 deviceStateItem.addSelectionListener(new SelectionAdapter() {
856 public void widgetSelected(SelectionEvent e) {
857 DeviceCommandDialog dlg;
858 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
859 "device-state.txt", shell);
860 dlg.open(mCurrentDevice);
864 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
865 appStateItem.setText("Dump &app state...");
866 appStateItem.setEnabled(false);
867 appStateItem.addSelectionListener(new SelectionAdapter() {
869 public void widgetSelected(SelectionEvent e) {
870 DeviceCommandDialog dlg;
871 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
872 dlg.open(mCurrentDevice);
876 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
877 radioStateItem.setText("Dump &radio state...");
878 radioStateItem.addSelectionListener(new SelectionAdapter() {
880 public void widgetSelected(SelectionEvent e) {
881 DeviceCommandDialog dlg;
882 dlg = new DeviceCommandDialog(
883 "cat /data/logs/radio.4 /data/logs/radio.3"
884 + " /data/logs/radio.2 /data/logs/radio.1"
885 + " /data/logs/radio",
886 "radio-state.txt", shell);
887 dlg.open(mCurrentDevice);
891 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
892 logCatItem.setText("Run &logcat...");
893 logCatItem.addSelectionListener(new SelectionAdapter() {
895 public void widgetSelected(SelectionEvent e) {
896 DeviceCommandDialog dlg;
897 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
899 dlg.open(mCurrentDevice);
903 // configure Action items based on current state
904 deviceMenu.addMenuListener(new MenuAdapter() {
906 public void menuShown(MenuEvent e) {
907 boolean deviceEnabled = mCurrentDevice != null;
908 screenShotItem.setEnabled(deviceEnabled);
909 explorerItem.setEnabled(deviceEnabled);
910 processItem.setEnabled(deviceEnabled);
911 deviceStateItem.setEnabled(deviceEnabled);
912 appStateItem.setEnabled(deviceEnabled);
913 radioStateItem.setEnabled(deviceEnabled);
914 logCatItem.setEnabled(deviceEnabled);
918 // tell the shell to use this menu
919 shell.setMenuBar(menuBar);
923 * Create the widgets in the main application window. The basic layout is a
924 * two-panel sash, with a scrolling list of VMs on the left and detailed
925 * output for a single VM on the right.
927 private void createWidgets(final Shell shell) {
928 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
931 * Create three areas: tool bar, split panels, status line
933 shell.setLayout(new GridLayout(1, false));
936 final Composite panelArea = new Composite(shell, SWT.BORDER);
938 // make the panel area absorb all space
939 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
942 mStatusLine = new Label(shell, SWT.NONE);
944 // make status line extend all the way across
945 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
947 mStatusLine.setText("Initializing...");
950 * Configure the split-panel area.
952 final PreferenceStore prefs = PrefsDialog.getStore();
954 Composite topPanel = new Composite(panelArea, SWT.NONE);
955 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
956 sash.setBackground(darkGray);
957 Composite bottomPanel = new Composite(panelArea, SWT.NONE);
959 panelArea.setLayout(new FormLayout());
961 createTopPanel(topPanel, darkGray);
963 if (useOldLogCatView()) {
964 createBottomPanel(bottomPanel);
966 createLogCatView(bottomPanel);
970 FormData data = new FormData();
971 data.top = new FormAttachment(0, 0);
972 data.bottom = new FormAttachment(sash, 0);
973 data.left = new FormAttachment(0, 0);
974 data.right = new FormAttachment(100, 0);
975 topPanel.setLayoutData(data);
977 final FormData sashData = new FormData();
978 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
979 sashData.top = new FormAttachment(0, prefs.getInt(
980 PREFERENCE_LOGSASH));
982 sashData.top = new FormAttachment(50,0); // 50% across
984 sashData.left = new FormAttachment(0, 0);
985 sashData.right = new FormAttachment(100, 0);
986 sash.setLayoutData(sashData);
988 data = new FormData();
989 data.top = new FormAttachment(sash, 0);
990 data.bottom = new FormAttachment(100, 0);
991 data.left = new FormAttachment(0, 0);
992 data.right = new FormAttachment(100, 0);
993 bottomPanel.setLayoutData(data);
995 // allow resizes, but cap at minPanelWidth
996 sash.addListener(SWT.Selection, new Listener() {
997 public void handleEvent(Event e) {
998 Rectangle sashRect = sash.getBounds();
999 Rectangle panelRect = panelArea.getClientArea();
1000 int bottom = panelRect.height - sashRect.height - 100;
1001 e.y = Math.max(Math.min(e.y, bottom), 100);
1002 if (e.y != sashRect.y) {
1003 sashData.top = new FormAttachment(0, e.y);
1004 if (prefs != null) {
1005 prefs.setValue(PREFERENCE_LOGSASH, e.y);
1012 // add a global focus listener for all the tables
1013 mTableListener = new TableFocusListener();
1015 // now set up the listener in the various panels
1016 if (useOldLogCatView()) {
1017 mLogPanel.setTableFocusListener(mTableListener);
1019 mEventLogPanel.setTableFocusListener(mTableListener);
1020 for (TablePanel p : mPanels) {
1022 p.setTableFocusListener(mTableListener);
1026 mStatusLine.setText("");
1030 * Populate the tool bar.
1032 private void createDevicePanelToolBar(ToolBar toolBar) {
1033 Display display = toolBar.getDisplay();
1035 // add "show heap updates" button
1036 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1037 mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1038 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1039 mTBShowHeapUpdates.setToolTipText("Show heap updates");
1040 mTBShowHeapUpdates.setEnabled(false);
1041 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1043 public void widgetSelected(SelectionEvent e) {
1044 if (mCurrentClient != null) {
1045 // boolean status = ((ToolItem)e.item).getSelection();
1046 // invert previous state
1047 boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1048 mCurrentClient.setHeapUpdateEnabled(enable);
1050 e.doit = false; // this has no effect?
1055 // add "dump HPROF" button
1056 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1057 mTBDumpHprof.setToolTipText("Dump HPROF file");
1058 mTBDumpHprof.setEnabled(false);
1059 mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display,
1060 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1061 mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1063 public void widgetSelected(SelectionEvent e) {
1064 mDevicePanel.dumpHprof();
1066 // this will make sure the dump hprof button is disabled for the current selection
1067 // as the client is already dumping an hprof file
1072 // add "cause GC" button
1073 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1074 mTBCauseGc.setToolTipText("Cause an immediate GC");
1075 mTBCauseGc.setEnabled(false);
1076 mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display,
1077 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1078 mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1080 public void widgetSelected(SelectionEvent e) {
1081 mDevicePanel.forceGcOnSelectedClient();
1085 new ToolItem(toolBar, SWT.SEPARATOR);
1087 // add "show thread updates" button
1088 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1089 mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1090 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1091 mTBShowThreadUpdates.setToolTipText("Show thread updates");
1092 mTBShowThreadUpdates.setEnabled(false);
1093 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1095 public void widgetSelected(SelectionEvent e) {
1096 if (mCurrentClient != null) {
1097 // boolean status = ((ToolItem)e.item).getSelection();
1098 // invert previous state
1099 boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1101 mCurrentClient.setThreadUpdateEnabled(enable);
1103 e.doit = false; // this has no effect?
1108 // add a start/stop method tracing
1109 mTracingStartImage = mDdmUiLibLoader.loadImage(display,
1110 DevicePanel.ICON_TRACING_START,
1111 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1112 mTracingStopImage = mDdmUiLibLoader.loadImage(display,
1113 DevicePanel.ICON_TRACING_STOP,
1114 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1115 mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1116 mTBProfiling.setToolTipText("Start Method Profiling");
1117 mTBProfiling.setEnabled(false);
1118 mTBProfiling.setImage(mTracingStartImage);
1119 mTBProfiling.addSelectionListener(new SelectionAdapter() {
1121 public void widgetSelected(SelectionEvent e) {
1122 mDevicePanel.toggleMethodProfiling();
1126 new ToolItem(toolBar, SWT.SEPARATOR);
1128 // add "kill VM" button; need to make this visually distinct from
1129 // the status update buttons
1130 mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1131 mTBHalt.setToolTipText("Halt the target VM");
1132 mTBHalt.setEnabled(false);
1133 mTBHalt.setImage(mDdmUiLibLoader.loadImage(display,
1134 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1135 mTBHalt.addSelectionListener(new SelectionAdapter() {
1137 public void widgetSelected(SelectionEvent e) {
1138 mDevicePanel.killSelectedClient();
1145 private void createTopPanel(final Composite comp, Color darkGray) {
1146 final PreferenceStore prefs = PrefsDialog.getStore();
1148 comp.setLayout(new FormLayout());
1150 Composite leftPanel = new Composite(comp, SWT.NONE);
1151 final Sash sash = new Sash(comp, SWT.VERTICAL);
1152 sash.setBackground(darkGray);
1153 Composite rightPanel = new Composite(comp, SWT.NONE);
1155 createLeftPanel(leftPanel);
1156 createRightPanel(rightPanel);
1158 FormData data = new FormData();
1159 data.top = new FormAttachment(0, 0);
1160 data.bottom = new FormAttachment(100, 0);
1161 data.left = new FormAttachment(0, 0);
1162 data.right = new FormAttachment(sash, 0);
1163 leftPanel.setLayoutData(data);
1165 final FormData sashData = new FormData();
1166 sashData.top = new FormAttachment(0, 0);
1167 sashData.bottom = new FormAttachment(100, 0);
1168 if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1169 sashData.left = new FormAttachment(0, prefs.getInt(
1172 // position the sash 380 from the right instead of x% (done by using
1173 // FormAttachment(x, 0)) in order to keep the sash at the same
1175 // from the left when the window is resized.
1176 // 380px is just enough to display the left table with no horizontal
1177 // scrollbar with the default font.
1178 sashData.left = new FormAttachment(0, 380);
1180 sash.setLayoutData(sashData);
1182 data = new FormData();
1183 data.top = new FormAttachment(0, 0);
1184 data.bottom = new FormAttachment(100, 0);
1185 data.left = new FormAttachment(sash, 0);
1186 data.right = new FormAttachment(100, 0);
1187 rightPanel.setLayoutData(data);
1189 final int minPanelWidth = 60;
1191 // allow resizes, but cap at minPanelWidth
1192 sash.addListener(SWT.Selection, new Listener() {
1193 public void handleEvent(Event e) {
1194 Rectangle sashRect = sash.getBounds();
1195 Rectangle panelRect = comp.getClientArea();
1196 int right = panelRect.width - sashRect.width - minPanelWidth;
1197 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1198 if (e.x != sashRect.x) {
1199 sashData.left = new FormAttachment(0, e.x);
1200 if (prefs != null) {
1201 prefs.setValue(PREFERENCE_SASH, e.x);
1209 private void createBottomPanel(final Composite comp) {
1210 final PreferenceStore prefs = PrefsDialog.getStore();
1213 Display display = comp.getDisplay();
1214 mClipboard = new Clipboard(display);
1216 LogColors colors = new LogColors();
1218 colors.infoColor = new Color(display, 0, 127, 0);
1219 colors.debugColor = new Color(display, 0, 0, 127);
1220 colors.errorColor = new Color(display, 255, 0, 0);
1221 colors.warningColor = new Color(display, 255, 127, 0);
1222 colors.verboseColor = new Color(display, 0, 0, 0);
1224 // set the preferences names
1225 LogPanel.PREFS_TIME = PREFS_COL_TIME;
1226 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1227 LogPanel.PREFS_PID = PREFS_COL_PID;
1228 LogPanel.PREFS_TAG = PREFS_COL_TAG;
1229 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1231 comp.setLayout(new GridLayout(1, false));
1233 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1235 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1236 mCreateFilterAction.item.setToolTipText("Create Filter");
1237 mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1238 "add.png", //$NON-NLS-1$
1239 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1240 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1242 public void widgetSelected(SelectionEvent e) {
1243 mLogPanel.addFilter();
1247 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1248 mEditFilterAction.item.setToolTipText("Edit Filter");
1249 mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1250 "edit.png", //$NON-NLS-1$
1251 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1252 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1254 public void widgetSelected(SelectionEvent e) {
1255 mLogPanel.editFilter();
1259 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1260 mDeleteFilterAction.item.setToolTipText("Delete Filter");
1261 mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1262 "delete.png", //$NON-NLS-1$
1263 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1264 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1266 public void widgetSelected(SelectionEvent e) {
1267 mLogPanel.deleteFilter();
1272 new ToolItem(toolBar, SWT.SEPARATOR);
1274 LogLevel[] levels = LogLevel.values();
1275 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1276 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1277 String name = levels[i].getStringValue();
1278 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1279 mLogLevelActions[i] = newAction;
1280 //newAction.item.setText(name);
1281 newAction.item.addSelectionListener(new SelectionAdapter() {
1283 public void widgetSelected(SelectionEvent e) {
1284 // disable the other actions and record current index
1285 for (int k = 0 ; k < mLogLevelActions.length; k++) {
1286 ToolItemAction a = mLogLevelActions[k];
1287 if (a == newAction) {
1290 // set the log level
1291 mLogPanel.setCurrentFilterLogLevel(k+2);
1293 a.setChecked(false);
1299 newAction.item.setToolTipText(name);
1300 newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1302 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1305 new ToolItem(toolBar, SWT.SEPARATOR);
1307 mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1308 mClearAction.item.setToolTipText("Clear Log");
1310 mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1311 "clear.png", //$NON-NLS-1$
1312 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1313 mClearAction.item.addSelectionListener(new SelectionAdapter() {
1315 public void widgetSelected(SelectionEvent e) {
1320 new ToolItem(toolBar, SWT.SEPARATOR);
1322 mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1323 mExportAction.item.setToolTipText("Export Selection As Text...");
1324 mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1325 "save.png", //$NON-NLS-1$
1326 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1327 mExportAction.item.addSelectionListener(new SelectionAdapter() {
1329 public void widgetSelected(SelectionEvent e) {
1337 // now create the log view
1338 mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
1340 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1342 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1343 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1344 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1347 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1348 if (fontStr != null) {
1350 FontData fdat = new FontData(fontStr);
1351 mLogPanel.setFont(new Font(display, fdat));
1352 } catch (IllegalArgumentException e) {
1353 // Looks like fontStr isn't a valid font representation.
1354 // We do nothing in this case, the logcat view will use the default font.
1355 } catch (SWTError e2) {
1356 // Looks like the Font() constructor failed.
1357 // We do nothing in this case, the logcat view will use the default font.
1361 mLogPanel.createPanel(comp);
1363 // and start the logcat
1364 mLogPanel.startLogCat(mCurrentDevice);
1367 private void createLogCatView(Composite parent) {
1368 mLogCatPanel = new LogCatPanel(new LogCatReceiver());
1369 mLogCatPanel.createPanel(parent);
1371 if (mCurrentDevice != null) {
1372 mLogCatPanel.deviceSelected(mCurrentDevice);
1377 * Create the contents of the left panel: a table of VMs.
1379 private void createLeftPanel(final Composite comp) {
1380 comp.setLayout(new GridLayout(1, false));
1381 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1382 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1383 createDevicePanelToolBar(toolBar);
1385 Composite c = new Composite(comp, SWT.NONE);
1386 c.setLayoutData(new GridData(GridData.FILL_BOTH));
1388 mDevicePanel = new DevicePanel(true /* showPorts */);
1389 mDevicePanel.createPanel(c);
1391 // add ourselves to the device panel selection listener
1392 mDevicePanel.addSelectionListener(this);
1396 * Create the contents of the right panel: tabs with VM information.
1398 private void createRightPanel(final Composite comp) {
1400 TabFolder tabFolder;
1402 comp.setLayout(new FillLayout());
1404 tabFolder = new TabFolder(comp, SWT.NONE);
1406 for (int i = 0; i < mPanels.length; i++) {
1407 if (mPanels[i] != null) {
1408 item = new TabItem(tabFolder, SWT.NONE);
1409 item.setText(mPanelNames[i]);
1410 item.setToolTipText(mPanelTips[i]);
1411 item.setControl(mPanels[i].createPanel(tabFolder));
1415 // add the emulator control panel to the folders.
1416 item = new TabItem(tabFolder, SWT.NONE);
1417 item.setText("Emulator Control");
1418 item.setToolTipText("Emulator Control Panel");
1419 mEmulatorPanel = new EmulatorControlPanel();
1420 item.setControl(mEmulatorPanel.createPanel(tabFolder));
1422 // add the event log panel to the folders.
1423 item = new TabItem(tabFolder, SWT.NONE);
1424 item.setText("Event Log");
1425 item.setToolTipText("Event Log");
1427 // create the composite that will hold the toolbar and the event log panel.
1428 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1429 item.setControl(eventLogTopComposite);
1430 eventLogTopComposite.setLayout(new GridLayout(1, false));
1432 // create the toolbar and the actions
1433 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1434 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1436 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1437 optionsAction.item.setToolTipText("Opens the options panel");
1438 optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1439 "edit.png", //$NON-NLS-1$
1440 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1442 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1443 clearAction.item.setToolTipText("Clears the event log");
1444 clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1445 "clear.png", //$NON-NLS-1$
1446 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1448 new ToolItem(toolbar, SWT.SEPARATOR);
1450 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1451 saveAction.item.setToolTipText("Saves the event log");
1452 saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1453 "save.png", //$NON-NLS-1$
1454 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1456 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1457 loadAction.item.setToolTipText("Loads an event log");
1458 loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1459 "load.png", //$NON-NLS-1$
1460 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1462 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1463 importBugAction.item.setToolTipText("Imports a bug report");
1464 importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1465 "importBug.png", //$NON-NLS-1$
1466 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1468 // create the event log panel
1469 mEventLogPanel = new EventLogPanel();
1471 // set the external actions
1472 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1476 mEventLogPanel.createPanel(eventLogTopComposite);
1479 private void createFileExplorer() {
1480 if (mExplorer == null) {
1481 mExplorerShell = new Shell(mDisplay);
1484 mExplorerShell.setLayout(new GridLayout(1, false));
1487 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1488 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1490 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1491 pullAction.item.setToolTipText("Pull File from Device");
1492 Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1493 if (image != null) {
1494 pullAction.item.setImage(image);
1496 // this is for debugging purpose when the icon is missing
1497 pullAction.item.setText("Pull"); //$NON-NLS-1$
1500 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1501 pushAction.item.setToolTipText("Push file onto Device");
1502 image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1503 if (image != null) {
1504 pushAction.item.setImage(image);
1506 // this is for debugging purpose when the icon is missing
1507 pushAction.item.setText("Push"); //$NON-NLS-1$
1510 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1511 deleteAction.item.setToolTipText("Delete");
1512 image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1513 if (image != null) {
1514 deleteAction.item.setImage(image);
1516 // this is for debugging purpose when the icon is missing
1517 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1520 ToolItemAction createNewFolderAction = new ToolItemAction(toolBar, SWT.PUSH);
1521 createNewFolderAction.item.setToolTipText("New Folder");
1522 image = mDdmUiLibLoader.loadImage("add.png", mDisplay); //$NON-NLS-1$
1523 if (image != null) {
1524 createNewFolderAction.item.setImage(image);
1526 // this is for debugging purpose when the icon is missing
1527 createNewFolderAction.item.setText("New Folder"); //$NON-NLS-1$
1531 mExplorer = new DeviceExplorer();
1532 mExplorer.setActions(pushAction, pullAction, deleteAction, createNewFolderAction);
1534 pullAction.item.addSelectionListener(new SelectionAdapter() {
1536 public void widgetSelected(SelectionEvent e) {
1537 mExplorer.pullSelection();
1540 pullAction.setEnabled(false);
1542 pushAction.item.addSelectionListener(new SelectionAdapter() {
1544 public void widgetSelected(SelectionEvent e) {
1545 mExplorer.pushIntoSelection();
1548 pushAction.setEnabled(false);
1550 deleteAction.item.addSelectionListener(new SelectionAdapter() {
1552 public void widgetSelected(SelectionEvent e) {
1553 mExplorer.deleteSelection();
1556 deleteAction.setEnabled(false);
1558 createNewFolderAction.item.addSelectionListener(new SelectionAdapter() {
1560 public void widgetSelected(SelectionEvent e) {
1561 mExplorer.createNewFolderInSelection();
1564 createNewFolderAction.setEnabled(false);
1566 Composite parent = new Composite(mExplorerShell, SWT.NONE);
1567 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1569 mExplorer.createPanel(parent);
1570 mExplorer.switchDevice(mCurrentDevice);
1572 mExplorerShell.addShellListener(new ShellListener() {
1573 public void shellActivated(ShellEvent e) {
1577 public void shellClosed(ShellEvent e) {
1579 mExplorerShell = null;
1582 public void shellDeactivated(ShellEvent e) {
1586 public void shellDeiconified(ShellEvent e) {
1590 public void shellIconified(ShellEvent e) {
1595 mExplorerShell.pack();
1596 setExplorerSizeAndPosition(mExplorerShell);
1597 mExplorerShell.open();
1599 if (mExplorerShell != null) {
1600 mExplorerShell.forceActive();
1606 * Set the status line. TODO: make this a stack, so we can safely have
1607 * multiple things trying to set it all at once. Also specify an expiration?
1609 public void setStatusLine(final String str) {
1611 mDisplay.asyncExec(new Runnable() {
1613 doSetStatusLine(str);
1616 } catch (SWTException swte) {
1617 if (!mDisplay.isDisposed())
1622 private void doSetStatusLine(String str) {
1623 if (mStatusLine.isDisposed())
1626 if (!mStatusLine.getText().equals(str)) {
1627 mStatusLine.setText(str);
1629 // try { Thread.sleep(100); }
1630 // catch (InterruptedException ie) {}
1634 public void displayError(final String msg) {
1636 mDisplay.syncExec(new Runnable() {
1638 MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1642 } catch (SWTException swte) {
1643 if (!mDisplay.isDisposed())
1648 private void enableButtons() {
1649 if (mCurrentClient != null) {
1650 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1651 mTBShowThreadUpdates.setEnabled(true);
1652 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1653 mTBShowHeapUpdates.setEnabled(true);
1654 mTBHalt.setEnabled(true);
1655 mTBCauseGc.setEnabled(true);
1657 ClientData data = mCurrentClient.getClientData();
1659 if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1660 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1661 mTBDumpHprof.setToolTipText("Dump HPROF file");
1663 mTBDumpHprof.setEnabled(false);
1664 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1667 if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1668 mTBProfiling.setEnabled(true);
1669 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1670 mTBProfiling.setToolTipText("Stop Method Profiling");
1671 mTBProfiling.setImage(mTracingStopImage);
1673 mTBProfiling.setToolTipText("Start Method Profiling");
1674 mTBProfiling.setImage(mTracingStartImage);
1677 mTBProfiling.setEnabled(false);
1678 mTBProfiling.setImage(mTracingStartImage);
1679 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1682 // list is empty, disable these
1683 mTBShowThreadUpdates.setSelection(false);
1684 mTBShowThreadUpdates.setEnabled(false);
1685 mTBShowHeapUpdates.setSelection(false);
1686 mTBShowHeapUpdates.setEnabled(false);
1687 mTBHalt.setEnabled(false);
1688 mTBCauseGc.setEnabled(false);
1690 mTBDumpHprof.setEnabled(false);
1691 mTBDumpHprof.setToolTipText("Dump HPROF file");
1693 mTBProfiling.setEnabled(false);
1694 mTBProfiling.setImage(mTracingStartImage);
1695 mTBProfiling.setToolTipText("Start Method Profiling");
1700 * Sent when a new {@link IDevice} and {@link Client} are selected.
1701 * @param selectedDevice the selected device. If null, no devices are selected.
1702 * @param selectedClient The selected client. If null, no clients are selected.
1704 * @see IUiSelectionListener
1706 public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1707 if (mCurrentDevice != selectedDevice) {
1708 mCurrentDevice = selectedDevice;
1709 for (TablePanel panel : mPanels) {
1710 if (panel != null) {
1711 panel.deviceSelected(mCurrentDevice);
1715 mEmulatorPanel.deviceSelected(mCurrentDevice);
1716 if (useOldLogCatView()) {
1717 mLogPanel.deviceSelected(mCurrentDevice);
1719 mLogCatPanel.deviceSelected(mCurrentDevice);
1721 if (mEventLogPanel != null) {
1722 mEventLogPanel.deviceSelected(mCurrentDevice);
1725 if (mExplorer != null) {
1726 mExplorer.switchDevice(mCurrentDevice);
1730 if (mCurrentClient != selectedClient) {
1731 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1732 mCurrentClient = selectedClient;
1733 for (TablePanel panel : mPanels) {
1734 if (panel != null) {
1735 panel.clientSelected(mCurrentClient);
1743 public void clientChanged(Client client, int changeMask) {
1744 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1745 Client.CHANGE_METHOD_PROFILING_STATUS) {
1746 if (mCurrentClient == client) {
1747 mDisplay.asyncExec(new Runnable() {
1749 // force refresh of the button enabled state.